set.seed(123)

Homoscedastic Error Variances

For the visualization of homocedastic and hetercedastic variance, we simulate:

\[ y \sim N \left ( -1 + 2x, 1 \right ) \]

The funnel-shaped heterocedasticy is simulated using:

\[ y \sim N \left ( -1 + 2x, \left ( 0.1 + 0.3 \left ( x + 3 \right ) \right )^{2} \right ) \]

par(mfrow = c(2, 2))

# Homecedastic variance
X <- seq(-2, 2, length.out = 300)

homecedastic_random <- function (x){
  rnorm(1, mean = -1 + 2 * x,  sd = 1)[1]
}

homo_y <- sapply(X, homecedastic_random)
homo_e <- (-1 + 2 * X) - homo_y

plot(X, homo_y, xlab='', ylab='', main='homocedastic variance')
abline(-1, 2, col = "red", lwd=2)
plot(X, homo_e, xlab='', ylab='', main='homocedastic variance errors')
abline(0, 0, col = "red", lwd=2)

heterocedastic_random <- function (x){
  rnorm(1, mean = -1 + 2 * x, sd = 0.1 + 0.3 * (x + 3))[1]
}

hetero_y <- sapply(X, heterocedastic_random)
hetero_e <- (-1 + 2 * X) - hetero_y

plot(X, hetero_y, xlab='', ylab='', main='funnel-shaped heterocedastic variance')
abline(-1, 2, col = "red", lwd=2)
plot(X, hetero_e, xlab='', ylab='', main='funnel-shaped heterocedastic variance errors')
abline(0, 0, col = "red", lwd=2)

Example 3.1 Munich Rent Index—Heteroscedastic Variances

par(mfrow = c(1, 2))

munich_rent_url <- "https://www.uni-goettingen.de/de/document/download/64c29c1b1fccb142cfa8f29a942a9e05.raw/rent99.raw"

munich_rent_index <- read.table(
  url(munich_rent_url),
  header = 1,
  colClasses = c(
    "numeric", "numeric", "numeric",
    "numeric", "factor", "factor",
    "factor", "factor", "factor"
  )
)

simple_reg <- lm(rent ~ area, data = munich_rent_index)

plot(munich_rent_index$area, munich_rent_index$rent, xlab = 'area in sqm', ylab = 'net rent in Euro')
abline(simple_reg, col = "red", lwd = 2)

predicted <- predict(simple_reg)

plot(munich_rent_index$area, predicted - munich_rent_index$rent, xlab = 'area in sqm', ylab = 'residuals')
abline(0, 0, col = "red", lwd = 2)

Uncorrelated Errors

Simulated autocorrelated errors with positive correlation are generated using:

\[ y_{i} = -1 + 2 x_{i} + \epsilon_{i} \] \[ \text{Where } \epsilon_{i} = 0.9 \epsilon_{i - 1} + \mu_{i} \wedge \mu_{i} \sim N(0, 0.5^{2}) \]

par(mfrow = c(1, 2))

n <- 100
X <- seq(-3, 3, length.out = n)
mu <- rnorm(n, mean = 0, sd = 0.5)

epsilon <- mu[1]
for(i in seq(2, n)){
  epsilon <- c(epsilon, 0.9 * epsilon[i - 1] + mu[i])
}

y_pred <- -1 + 2 * X + epsilon

plot(X, y_pred, xlab = 'x', ylab = '', main = 'observations and regression line')
abline(-1, 2, col = "red", lwd = 2)

plot(epsilon, xlab = 'i', ylab = '', main = 'time series of the errors')
abline(0, 0, col = "red", lwd = 2)

Simulated autocorrelated errors with negative correlation are generated using:

\[ y_{i} = -1 + 2 x_{i} + \epsilon_{i} \] \[ \text{Where } \epsilon_{i} = - 0.9 \epsilon_{i - 1} + \mu_{i} \wedge \mu_{i} \sim N(0, 0.5^{2}) \]

par(mfrow = c(1, 2))

n <- 100
X <- seq(-3, 3, length.out = n)
mu <- rnorm(n, mean = 0, sd = 0.5)

epsilon <- mu[1]
for(i in seq(2, n)){
  epsilon <- c(epsilon, -0.9 * epsilon[i - 1] + mu[i])
}

y_pred <- -1 + 2 * X + epsilon

plot(X, y_pred, xlab = 'x', ylab = '', main = 'observations and regression line')
abline(-1, 2, col = "red", lwd = 2)

plot(epsilon, xlab = 'i', ylab = '', main = 'time series of the errors')
abline(0, 0, col = "red", lwd = 2)

Mispecified model is simulated using:

\[ y_{i} = \sin(x_{i}) + x_{i} + \epsilon_{i} \] \[ \text{Where } \epsilon_{i} \sim N(0, 0.3^{2}) \]

par(mfrow = c(2, 2))

n <- 100
X <- seq(-3, 3, length.out = n)
epsilon <- rnorm(n, mean = 0, sd = 0.3)

y <- sin(X) + X + epsilon

plot(X, y, xlab = '', ylab = '', main = 'observations and true function')
lines(X, sin(X) + X, col = "red", lwd = 2)

linear_model <- lm(y ~ X)

plot(X, y, xlab = '', ylab = '', main = 'observations and regression line')
abline(linear_model, col = "red", lwd = 2)

y_pred <- predict(linear_model, data = X)
res <- y_pred - y
plot(X, res, xlab = '', ylab = '', main = 'residuals')
abline(0, 0, col = "red", lwd = 2)

Additivity of Errors

We simulate data using:

\[ y_{i} = \exp(1 + x_{i1} - x_{i2} + \epsilon_{i}) \]

With: \[ \epsilon_{i} \sim N \left ( 0, {0.4}^{2} \right ) \]

We plot this model:

par(mfrow = c(2, 2))

n <- 50

x1 <- seq(0, 3, length.out = n)
x2 <- seq(0, 3, length.out = n)
x_grid <- expand.grid(x1 = x1, x2 = x2)
epsilon <- rnorm(n^2, mean = 0, sd = 0.4)
y <- exp(1 + x_grid$x1 - x_grid$x2 + epsilon)

plot(
  x_grid$x1, y,
  main = 'scatter plot: y versus x1',
  xlab = 'x1',
  ylab = 'y'
)
plot(
  x_grid$x2, y,
  main = 'scatter plot: y versus x2',
  xlab = 'x2',
  ylab = 'y'
)

plot(
  x_grid$x1, log(y),
  main = 'scatter plot: log(y) versus x1',
  xlab = 'x1',
  ylab = 'log(y)'
)
plot(
  x_grid$x2, log(y),
  main = 'scatter plot: log(y) versus x2',
  xlab = 'x2',
  ylab = 'log(y)'
)

Example 3.2 Supermarket Scanner Data

Data not available online.

Example 3.3 Munich Rent Index — Variable Transformation

Importing data using script:

source("import_data/munich_rent_index.R")

We do the regression model:

\[ \text{rentsqm}_{i} = \beta_{0} + \beta_{1} \cdot f(\text{area}_{i}) + \epsilon_{i} \]

With both \(f(\cdot)\):

\[ f(\text{area}_{i}) = \text{area}_{i} \]

For linear model and:

\[ f(\text{area}_{i}) = \frac{1}{\text{area}_{i}} \]

For future use we define those models:

Model 1 (\(M1\)):

\[ \mathbb{E}(\text{rentqsm}) = \beta_{0} + \beta_{1} \text{area} \]

Model 2 (\(M2\)):

\[ \mathbb{E}(\text{rentqsm}) = \beta_{0} + \beta_{1} \frac{1}{\text{area}} \]

For nonlinear relationship between \(\text{rentsqm}\) and \(\text{area}\). The plotted graph below are those two models with the left panels the linear regression without the transformation and the right panels with the inverse transformation.

par(mfrow = c(3, 2))

m1 <- lm(rentsqm ~ area, data = munich_rent_index)
summary(m1)

Call:
lm(formula = rentsqm ~ area, data = munich_rent_index)

Residuals:
    Min      1Q  Median      3Q     Max 
-6.9622 -1.5737 -0.1102  1.5861  9.9674 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  9.46883    0.12426   76.20   <2e-16 ***
area        -0.03499    0.00174  -20.11   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 2.291 on 3080 degrees of freedom
Multiple R-squared:  0.1161,    Adjusted R-squared:  0.1158 
F-statistic: 404.5 on 1 and 3080 DF,  p-value: < 2.2e-16
m2 <- lm(rentsqm ~ I(1 / area), data = munich_rent_index)
summary(m2)

Call:
lm(formula = rentsqm ~ I(1/area), data = munich_rent_index)

Residuals:
    Min      1Q  Median      3Q     Max 
-7.3963 -1.5733 -0.0921  1.5824 10.1287 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   4.7321     0.1084   43.65   <2e-16 ***
I(1/area)   140.1783     5.9287   23.64   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 2.241 on 3080 degrees of freedom
Multiple R-squared:  0.1536,    Adjusted R-squared:  0.1533 
F-statistic:   559 on 1 and 3080 DF,  p-value: < 2.2e-16
plot_rentsqm_area <- function(title) {
  plot(
    munich_rent_index$area,
    munich_rent_index$rentsqm,
    main = title,
    xlab = 'area in sqm',
    ylab = 'rent per sqm'
  )
}

seq_area <- seq(min(munich_rent_index$area), max(munich_rent_index$area), length.out = 100)

plot_rentsqm_area('rent per sqm vs. area')
m1_beta <- m1$coefficients
lines(seq_area, m1_beta[1] + m1_beta[2] * seq_area, col = 'red', lwd = 2)

plot_rentsqm_area('f(area) = 1/area')
m2_beta <- m2$coefficients
lines(seq_area, m2_beta[1] + m2_beta[2] * 1 / seq_area, col = 'red', lwd = 2)

plot_residuals <- function(model) {
  plot(
    munich_rent_index$area,
    model$residuals,
    main = 'residuals',
    xlab = 'area in sqm',
    ylab = 'residuals'
  )
  abline(0, 0, col = 'red', lwd = 2)
}

plot_residuals(m1)
plot_residuals(m2)

plot_av_residuals <- function(model) {
  av_residuals <- tapply(
    model$residuals, munich_rent_index$area,
    mean
  )
  plot(
    unique(munich_rent_index$area),
    av_residuals,
    main = 'average residuals',
    xlab = 'area in sqm',
    ylab = 'residuals'
  )
  abline(0, 0, col = 'red', lwd = 2)
}

plot_av_residuals(m1)
plot_av_residuals(m2)

Example 3.4 Munich Rent Index — Polynomial Regression

For polynomial regressions we adjust two different model. Of second-order (Model 3 (\(M3\))):

\[ \mathbb{E}(\text{rentqsm}) = \beta_{0} + \beta_{1} \text{area} + \beta_{2} \text{area}^{2} \]

And third-order (Model 4 (\(M4\))):

\[ \mathbb{E}(\text{rentqsm}) = \beta_{0} + \beta_{1} \text{area} + \beta_{2} \text{area}^{2} + \beta_{3} \text{area}^{3} \]

par(mfrow = c(2, 2))

m3 <- lm(rentsqm ~ area + I(area^2), data = munich_rent_index)
summary(m3)

Call:
lm(formula = rentsqm ~ area + I(area^2), data = munich_rent_index)

Residuals:
    Min      1Q  Median      3Q     Max 
-7.2150 -1.5816 -0.0915  1.5869  9.9392 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  1.183e+01  2.684e-01  44.081   <2e-16 ***
area        -1.057e-01  7.351e-03 -14.380   <2e-16 ***
I(area^2)    4.707e-04  4.758e-05   9.892   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 2.255 on 3079 degrees of freedom
Multiple R-squared:  0.1433,    Adjusted R-squared:  0.1428 
F-statistic: 257.6 on 2 and 3079 DF,  p-value: < 2.2e-16
m4 <- lm(rentsqm ~ area + I(area^2) + I(area^3), data = munich_rent_index)
summary(m4)

Call:
lm(formula = rentsqm ~ area + I(area^2) + I(area^3), data = munich_rent_index)

Residuals:
    Min      1Q  Median      3Q     Max 
-7.4269 -1.5854 -0.1101  1.5948 10.0670 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  1.428e+01  5.730e-01  24.915  < 2e-16 ***
area        -2.175e-01  2.432e-02  -8.946  < 2e-16 ***
I(area^2)    1.981e-03  3.167e-04   6.254 4.54e-10 ***
I(area^3)   -6.105e-06  1.266e-06  -4.823 1.49e-06 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 2.247 on 3078 degrees of freedom
Multiple R-squared:  0.1497,    Adjusted R-squared:  0.1489 
F-statistic: 180.7 on 3 and 3078 DF,  p-value: < 2.2e-16
plot_rentsqm_area('rent per sqm vs. area')
m3_beta <- m3$coefficients
lines(seq_area, m3_beta[1] + m3_beta[2] * seq_area + m3_beta[3] * seq_area ^ 2, col = 'red', lwd = 2)

plot_rentsqm_area('f(area) = 1/area')
m4_beta <- m4$coefficients
lines(seq_area, m4_beta[1] + m4_beta[2] * seq_area + m4_beta[3] * seq_area ^ 2 + m4_beta[4] * seq_area ^ 3, col = 'red', lwd = 2)

plot_residuals(m3)
plot_residuals(m4)

Example 3.5 Munich Rent Index — Additive Models

We define the following aditive models. Additive model 1:

\[ \mathcal{E}(\text{rentsqm}_{i}) = \beta_{0} + \beta_{1} \cdot \frac{1}{\text{area}_{i}} + \beta_{2} \cdot \text{yearc}_{i} \]

And a second one with \(\text{yearc}\) as a polinomial of degree 3:

\[ \mathcal{E}(\text{rentsqm}_{i}) = \beta_{0} + \beta_{1} \cdot \frac{1}{\text{area}_{i}} + \beta_{2} \cdot \text{yearc}_{i} + \beta_{3} \cdot {\text{yearc}_{i}}^{2} + \beta_{4} \cdot {\text{yearc}_{i}}^{3} \]

additive_m1 <- lm(rentsqm ~ I(1/area) + yearc, data = munich_rent_index)
summary(additive_m1)

Call:
lm(formula = rentsqm ~ I(1/area) + yearc, data = munich_rent_index)

Residuals:
    Min      1Q  Median      3Q     Max 
-7.2682 -1.4894 -0.1401  1.3935  9.0582 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) -65.406008   3.351088  -19.52   <2e-16 ***
I(1/area)   119.360735   5.636182   21.18   <2e-16 ***
yearc         0.036033   0.001721   20.94   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 2.097 on 3079 degrees of freedom
Multiple R-squared:  0.2591,    Adjusted R-squared:  0.2586 
F-statistic: 538.5 on 2 and 3079 DF,  p-value: < 2.2e-16
additive_m2 <- lm(rentsqm ~ I(1/area) + yearc + I(yearc ^ 2)+ I(yearc ^ 3), data = munich_rent_index)
summary(additive_m2)

Call:
lm(formula = rentsqm ~ I(1/area) + yearc + I(yearc^2) + I(yearc^3), 
    data = munich_rent_index)

Residuals:
    Min      1Q  Median      3Q     Max 
-6.9920 -1.3705 -0.1354  1.3711  8.2834 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  2.942e+04  2.993e+04   0.983    0.326    
I(1/area)    1.296e+02  5.536e+00  23.406   <2e-16 ***
yearc       -4.330e+01  4.592e+01  -0.943    0.346    
I(yearc^2)   2.119e-02  2.348e-02   0.902    0.367    
I(yearc^3)  -3.445e-06  4.002e-06  -0.861    0.389    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 2.039 on 3077 degrees of freedom
Multiple R-squared:    0.3, Adjusted R-squared:  0.2991 
F-statistic: 329.7 on 4 and 3077 DF,  p-value: < 2.2e-16

A third model is specified using the truncated year of construction (\(\text{yearc} - 1900\)):

munich_rent_index$yearco <- munich_rent_index$yearc - 1900
additive_m3 <- lm(rentsqm ~ I(1/area) + yearco + I(yearco ^ 2)+ I(yearco ^ 3), data = munich_rent_index)
summary(additive_m3)

Call:
lm(formula = rentsqm ~ I(1/area) + yearco + I(yearco^2) + I(yearco^3), 
    data = munich_rent_index)

Residuals:
    Min      1Q  Median      3Q     Max 
-6.9920 -1.3705 -0.1354  1.3711  8.2834 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  5.417e+00  4.779e-01  11.335  < 2e-16 ***
I(1/area)    1.296e+02  5.536e+00  23.406  < 2e-16 ***
yearco      -9.434e-02  3.384e-02  -2.788  0.00534 ** 
I(yearco^2)  1.553e-03  6.728e-04   2.308  0.02105 *  
I(yearco^3) -3.445e-06  4.002e-06  -0.861  0.38947    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 2.039 on 3077 degrees of freedom
Multiple R-squared:    0.3, Adjusted R-squared:  0.2991 
F-statistic: 329.7 on 4 and 3077 DF,  p-value: < 2.2e-16

As in the book, we show the effect of year of construction in the polynomial of degree 3:

par(mfrow = c(1, 2))
beta_add_m2 <- additive_m2$coefficients

yearc_seq <- seq(min(munich_rent_index$yearc), max(munich_rent_index$yearc), length.out = 100)

plot_effect <- function(beta, year_seq, title){
  plot(
    year_seq,
    beta[3] * year_seq + beta[4] * year_seq^2 + beta[5] * year_seq^3,
    main = title,
    xlab = 'year of construction',
    ylab = 'effect of year of construction',
    type = 'l',
    col = 'darkblue',
    lwd = 2
  )
}

plot_effect(beta_add_m2, yearc_seq, 'effect of year of construction')

beta_add_m3 <- additive_m3$coefficients
truncate_yearc_seq <- seq(min(munich_rent_index$yearco), max(munich_rent_index$yearco), length.out = 100)
plot_effect(beta_add_m3, truncate_yearc_seq, 'effect of year of construction, coded from 18 to 96')

A new model is defined using the orthogonal polynomial of year of construction:

munich_rent_index$areainv <- (1 / munich_rent_index$area) - mean(1 / munich_rent_index$area)
poly_year <- poly(munich_rent_index$yearco, 3)
munich_rent_index$yearcc <- poly_year[, 1]
munich_rent_index$yearcc2 <- poly_year[, 2]
munich_rent_index$yearcc3 <- poly_year[, 3]
additive_m4 <- lm(rentsqm ~ areainv + yearcc + yearcc2 + yearcc3, data = munich_rent_index)
summary(additive_m4)

Call:
lm(formula = rentsqm ~ areainv + yearcc + yearcc2 + yearcc3, 
    data = munich_rent_index)

Residuals:
    Min      1Q  Median      3Q     Max 
-6.9920 -1.3705 -0.1354  1.3711  8.2834 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)   7.11126    0.03674 193.575   <2e-16 ***
areainv     129.57166    5.53582  23.406   <2e-16 ***
yearcc       43.93838    2.07260  21.200   <2e-16 ***
yearcc2      27.53892    2.05958  13.371   <2e-16 ***
yearcc3      -1.75582    2.03998  -0.861    0.389    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 2.039 on 3077 degrees of freedom
Multiple R-squared:    0.3, Adjusted R-squared:  0.2991 
F-statistic: 329.7 on 4 and 3077 DF,  p-value: < 2.2e-16

Now we calculate the partial residuals for \(\text{area}\) and \(\text{yearc}\) define by:

\[ \hat{\epsilon}_{\text{area}, i} = \text{rentsqm}_{i} - \hat{\beta_{0}} - \hat{f}_{2}(\text{yearc}_{i}) \]

With the effect \(f(\text{yearc}_{i})\):

\[ \hat{f}_{2}(\text{yearc}) = \beta_{2} \cdot \text{yearc}_{i} + \beta_{3} \cdot {\text{yearc}_{i}}^{2} + \beta_{4} \cdot {\text{yearc}_{i}}^{3} \]

And:

\[ \hat{\epsilon}_{\text{yearc}, i} = \text{rentsqm}_{i} - \hat{\beta_{0}} - \hat{f}_{1}(\text{area}_{i}) \]

With the effect \(f(\text{yearc}_{i})\):

\[ \hat{f}_{1}(\text{yearc}) = \beta_{1} \cdot \frac{1}{\text{area}_{i}} \]

par(mfrow = c(1, 2))

beta_add_m4 <- additive_m4$coefficients

area_seq <- seq(min(munich_rent_index$area), max(munich_rent_index$area), length.out = 100)

area_effect <- function(area){
  inv_area <- 1 / area
  cent_area <- inv_area - mean(inv_area)
  beta_add_m4[2] * cent_area
}

yearc_effect <- function(yearc){
  poly_yearc <- predict(poly_year, yearc - 1900)
  beta_add_m4[3] * poly_yearc[, 1] + beta_add_m4[4] * poly_yearc[, 2] + beta_add_m4[5] * poly_yearc[, 3]
}

residual_area <- munich_rent_index$rentsqm - beta_add_m4[1] - yearc_effect(munich_rent_index$yearc)
plot(
  munich_rent_index$area,
  residual_area,
  main = 'effect of area',
  xlab = 'area in sqm',
  ylab = 'effect of area / partial residuals'
)
lines(area_seq, area_effect(area_seq), col = 'red', lwd = 2)

residual_year <- munich_rent_index$rentsqm - beta_add_m4[1] - area_effect(munich_rent_index$area)
plot(
  munich_rent_index$yearc,
  residual_year,
  main = 'effect of year of construction',
  xlab = 'year of construction',
  ylab = 'effect of year of construction / partial residuals'
)
lines(yearc_seq, yearc_effect(yearc_seq), col = 'red', lwd = 2)

Example 3.6 Munich Rent Index — Effect Coding

We adjust the regression model with the coding values of location:

\[ \mathbb{E}(\text{rentsqm}_{i}) = \beta_{0} + \beta_{1} \cdot \text{glocation}_{1} + \beta_{2} \cdot \text{tlocation}_{1} \]

munich_rent_index$glocation <- 0
munich_rent_index$glocation[munich_rent_index$location == 2] <- 1
munich_rent_index$glocation[munich_rent_index$location == 1] <- -1

munich_rent_index$tlocation <- 0
munich_rent_index$tlocation[munich_rent_index$location == 3] <- 1
munich_rent_index$tlocation[munich_rent_index$location == 1] <- -1

cod_model <- lm(
  rentsqm ~ glocation + tlocation,
  data = munich_rent_index
)
summary(cod_model)

Call:
lm(formula = rentsqm ~ glocation + tlocation, data = munich_rent_index)

Residuals:
    Min      1Q  Median      3Q     Max 
-6.8564 -1.8376 -0.1074  1.7157 10.4494 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  7.46704    0.09638  77.477  < 2e-16 ***
glocation   -0.19479    0.10445  -1.865 0.062285 .  
tlocation    0.70529    0.18558   3.800 0.000147 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 2.426 on 3079 degrees of freedom
Multiple R-squared:  0.008867,  Adjusted R-squared:  0.008223 
F-statistic: 13.77 on 2 and 3079 DF,  p-value: 1.109e-06

Example 3.7 Munich Rent Index — Interaction with Quality of Kitchen

We import the new updated model of 2001, available at official page of the dataset as all the datasets:

munich_rent_url_01 <- "https://www.uni-goettingen.de/de/document/download/e00d039e9c1f28ab404b27b0b14931f1.raw/rent98_00.raw"

munich_rent_index_01 <- read.table(
  url(munich_rent_url_01),
  header = 1,
  colClasses = c(
    "numeric", "numeric", "numeric",
    "numeric", "integer", "integer",
    "integer", "integer", "integer",
    "integer", "numeric", "numeric",
    "numeric"
  )
)

summary(munich_rent_index_01)
   rent_euro        rentsqm_euro          area            yearc     
 Min.   :  12.83   Min.   : 0.1841   Min.   : 20.00   Min.   :1918  
 1st Qu.: 320.86   1st Qu.: 5.2601   1st Qu.: 52.00   1st Qu.:1939  
 Median : 424.12   Median : 6.8614   Median : 66.00   Median :1960  
 Mean   : 456.94   Mean   : 7.0274   Mean   : 67.92   Mean   :1956  
 3rd Qu.: 552.40   3rd Qu.: 8.7292   3rd Qu.: 82.00   3rd Qu.:1972  
 Max.   :1837.89   Max.   :17.6688   Max.   :243.00   Max.   :1999  
   glocation        tlocation          nkitchen          pkitchen      
 Min.   :0.0000   Min.   :0.00000   Min.   :0.00000   Min.   :0.00000  
 1st Qu.:0.0000   1st Qu.:0.00000   1st Qu.:0.00000   1st Qu.:0.00000  
 Median :0.0000   Median :0.00000   Median :0.00000   Median :0.00000  
 Mean   :0.3907   Mean   :0.02516   Mean   :0.09888   Mean   :0.04004  
 3rd Qu.:1.0000   3rd Qu.:0.00000   3rd Qu.:0.00000   3rd Qu.:0.00000  
 Max.   :1.0000   Max.   :1.00000   Max.   :1.00000   Max.   :1.00000  
     eboden           year01           yearc2            yearc3         
 Min.   :0.0000   Min.   :0.0000   Min.   :3678724   Min.   :7.060e+09  
 1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:3759721   1st Qu.:7.290e+09  
 Median :0.0000   Median :0.0000   Median :3841600   Median :7.530e+09  
 Mean   :0.2986   Mean   :0.3257   Mean   :3828362   Mean   :7.493e+09  
 3rd Qu.:1.0000   3rd Qu.:1.0000   3rd Qu.:3888784   3rd Qu.:7.670e+09  
 Max.   :1.0000   Max.   :1.0000   Max.   :3996001   Max.   :7.990e+09  
    invarea        
 Min.   :0.004115  
 1st Qu.:0.012195  
 Median :0.015152  
 Mean   :0.016904  
 3rd Qu.:0.019231  
 Max.   :0.050000  

And we adjust the model:

\[ \begin{matrix} \widehat{\text{rentsqm}}_{i} = & \beta_{0} + \beta_{1} \text{areainvc}_{i} + \beta_{2} \text{yearco}_{i} + \beta_{3} \text{yearco2}_{i} + \beta_{4} \text{yearco3}_{i} \\ & + \beta_{5} \text{year01}_{i} + \beta_{6} \text{nkitchen}_{i} + \beta_{7} \text{pkitchen}_{i} \\ & + \beta_{8} \text{nkitchen}_{i} \cdot \text{year01}_{i} + \beta_{9} \text{pkitchen}_{i} \cdot \text{year01}_{i} \end{matrix} \]

munich_rent_index_01$areainvc <- munich_rent_index_01$invarea - mean(munich_rent_index_01$invarea)
munich_rent_index_01$yearco <- munich_rent_index_01$yearc - mean(munich_rent_index_01$yearc)
poly_year_01 <- poly(munich_rent_index_01$yearco, 3)
munich_rent_index_01$yearco <- poly_year_01[, 1]
munich_rent_index_01$yearco2 <- poly_year_01[, 2]
munich_rent_index_01$yearco3 <- poly_year_01[, 3]

interaction_model <- lm(
  rentsqm_euro ~ areainvc + yearco + yearco2 + yearco3 + year01 + nkitchen + pkitchen + nkitchen * year01 + pkitchen * year01,
  data = munich_rent_index_01
)

summary(interaction_model)

Call:
lm(formula = rentsqm_euro ~ areainvc + yearco + yearco2 + yearco3 + 
    year01 + nkitchen + pkitchen + nkitchen * year01 + pkitchen * 
    year01, data = munich_rent_index_01)

Residuals:
    Min      1Q  Median      3Q     Max 
-7.6303 -1.2647 -0.1072  1.2414 10.5233 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)       6.95424    0.03841 181.072  < 2e-16 ***
areainvc        123.71308    4.42630  27.950  < 2e-16 ***
yearco           49.42282    2.02589  24.396  < 2e-16 ***
yearco2          29.45781    2.02386  14.555  < 2e-16 ***
yearco3          -1.03886    1.97176  -0.527 0.598311    
year01           -0.25174    0.06678  -3.770 0.000166 ***
nkitchen          0.91567    0.12172   7.523 6.43e-14 ***
pkitchen          1.09081    0.17849   6.111 1.07e-09 ***
year01:nkitchen   0.39826    0.20922   1.904 0.057033 .  
year01:pkitchen   0.73511    0.32864   2.237 0.025346 *  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 1.963 on 4561 degrees of freedom
Multiple R-squared:  0.3397,    Adjusted R-squared:  0.3384 
F-statistic: 260.7 on 9 and 4561 DF,  p-value: < 2.2e-16

Example 3.8 Munich Rent Index — Interaction Between Living Area and Location

The model to adjust ius defined as:

\[ \mathbb{E}(\text{rentsqm}_{i}) = \beta_{0} + \beta_{1} \cdot \text{tlocation} + \beta_{2} \cdot \overline{\frac{1}{\text{area}}} + \beta{3} \cdot \text{area} \cdot \text{tlocation} \]

We use the dataset for average or top location only and change the dummy variable \(\text{tlocation}\) to \(0\) (average) and \(1\) (top). Finally, we visualize the effect as described in the book.

par(mfrow = c(1, 2))
at_rent <- subset(munich_rent_index, location == 1 | location == 3)
at_rent$tlocation[at_rent$location == 1] <- 0
summary(at_rent)
      rent            rentsqm            area            yearc      location
 Min.   :  54.92   Min.   : 1.417   Min.   : 20.00   Min.   :1918   1:1794  
 1st Qu.: 314.62   1st Qu.: 5.276   1st Qu.: 50.00   1st Qu.:1954   2:   0  
 Median : 418.00   Median : 6.849   Median : 65.00   Median :1962   3:  78  
 Mean   : 444.22   Mean   : 7.007   Mean   : 66.02   Mean   :1959           
 3rd Qu.: 537.42   3rd Qu.: 8.652   3rd Qu.: 80.00   3rd Qu.:1972           
 Max.   :1459.23   Max.   :15.428   Max.   :155.00   Max.   :1997           
                                                                            
    bath          kitchen         cheating          district        yearco     
 Mode :logical   Mode :logical   Mode :logical   623    :  53   Min.   :18.00  
 FALSE:1761      FALSE:1785      FALSE:148       1013   :  37   1st Qu.:54.00  
 TRUE :111       TRUE :87        TRUE :1724      713    :  32   Median :62.00  
                                                 1132   :  31   Mean   :59.36  
                                                 1712   :  30   3rd Qu.:72.00  
                                                 2024   :  30   Max.   :97.00  
                                                 (Other):1659                  
    areainv               yearcc             yearcc2          
 Min.   :-0.0105206   Min.   :-0.030935   Min.   :-0.0183373  
 1st Qu.:-0.0044722   1st Qu.:-0.001862   1st Qu.:-0.0147283  
 Median :-0.0015876   Median : 0.004598   Median :-0.0067317  
 Mean   : 0.0002152   Mean   : 0.002465   Mean   :-0.0008428  
 3rd Qu.: 0.0030278   3rd Qu.: 0.012674   3rd Qu.: 0.0116794  
 Max.   : 0.0330278   Max.   : 0.032863   Max.   : 0.0537875  
                                                              
    yearcc3             glocation         tlocation      
 Min.   :-0.0186291   Min.   :-1.0000   Min.   :0.00000  
 1st Qu.:-0.0172309   1st Qu.:-1.0000   1st Qu.:0.00000  
 Median :-0.0056305   Median :-1.0000   Median :0.00000  
 Mean   :-0.0004071   Mean   :-0.9583   Mean   :0.04167  
 3rd Qu.: 0.0096124   3rd Qu.:-1.0000   3rd Qu.:0.00000  
 Max.   : 0.0588009   Max.   : 0.0000   Max.   :1.00000  
                                                         
area_location_model <- lm(
  rentsqm ~ tlocation + areainv + area:tlocation,
  data = at_rent
)
summary(area_location_model)

Call:
lm(formula = rentsqm ~ tlocation + areainv + area:tlocation, 
    data = at_rent)

Residuals:
    Min      1Q  Median      3Q     Max 
-7.2380 -1.4435 -0.1189  1.4788  7.1162 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)    6.911e+00  4.967e-02 139.131   <2e-16 ***
tlocation      7.722e-01  7.280e-01   1.061    0.289    
areainv        1.431e+02  7.434e+00  19.252   <2e-16 ***
tlocation:area 1.001e-02  8.612e-03   1.163    0.245    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 2.101 on 1868 degrees of freedom
Multiple R-squared:  0.1775,    Adjusted R-squared:  0.1762 
F-statistic: 134.4 on 3 and 1868 DF,  p-value: < 2.2e-16
beta_al <- area_location_model$coefficients

f1_area <- function(area){
  areainvc <- (1 / area) - mean(1 / area)
  beta_al[3] * areainvc
}

f2_area <- function(area){
  beta_al[4] * area
}

small_effect <- f1_area(area_seq)
big_effect <- beta_al[2] + f1_area(area_seq) + f2_area(area_seq)

ylim <- c(min(c(small_effect, big_effect)), max(c(small_effect, big_effect)))

plot(
  area_seq,
  small_effect,
  main = 'area effects based on a linear interaction',
  xlab = 'area in sqm',
  ylab = 'area effects',
  ylim = ylim,
  type = 'l',
  col = 'darkblue',
  lwd = 2
)
lines(area_seq, big_effect, col = 'red', lwd = 2)

plot(
  area_seq,
  beta_al[2] + f2_area(area_seq),
  main = 'varying effect of top location',
  xlab = 'area in sqm',
  ylab = 'Varying effect of top location',
  type = 'l',
  col = 'darkblue',
  lwd = 2
)

Example 3.9 Orthogonal Design

Just for exemplify, we define the orthogonal process describe as an R function:

\[ \tilde{x}^{j} = x^{j} - \widetilde{X}_{j} \left ( {\widetilde{X}_{j}}^{T} \widetilde{X}_{j} \right )^{-1} {\widetilde{X}_{j}}^{T} x^{j} \]

orthogonal_design <- function(x){
  x_out <- cbind(x[, 1] - mean(x[, 1]), matrix(0, nrow = nrow(x), ncol = ncol(x) - 1))
  for (i in 2:ncol(x)){
    x_hat <- as.matrix(x_out[, 1:(i-1)])
    x_inv <- solve(t(x_hat) %*% x_hat)
    x_out[, i] <- x[, i] - x_hat %*% x_inv %*% t(x_hat) %*% x[, i]
  }
  x_out
}

We test this function on the polynomial of the \(\text{yearc}\) variable:

poly_test <- poly(munich_rent_index$yearc, 3, raw = TRUE)
orthogonal_test <- orthogonal_design(poly_test)

# And check it
c(
  orthogonal_test[, 1] %*% orthogonal_test[, 2],
  orthogonal_test[, 2] %*% orthogonal_test[, 3],
  orthogonal_test[, 1] %*% orthogonal_test[, 3]
)
[1] 7.550556e-05 8.482947e+05 2.602290e+00

Why did it not result?

Example 3.10 Munich Rent Index — Comparison of Models Using \({R}^{2}\)

We compare the models: \(M1\), \(M2\), \(M3\), and \(M4\) using its coefficient of determination \(R^{2}\)

coeff_deter <- data.frame(
  `R Squared` = c(summary(m1)$r.squared, summary(m2)$r.squared, summary(m3)$r.squared, summary(m4)$r.squared),
  row.names = c("M1", "M2", "M3", "M4")
)
coeff_deter

Example 3.11 Simple Linear Regression

For the model:

\[ y_{i} = \beta x_{i} + \epsilon_{i} \]

We can verify:

\[ \left ( {X_{n}}^{T} X_{n} \right )^{-1} \to 0 \]

And:

\[ \max_{i=1,...,n} {x_{i}}^{T} \left ( {X_{n}}^{T} X_{n} \right )^{-1} x_{i} \to 0 \text{ for } n \to \infty \]

\(x_{i} = i\):

\[ \left ( {X_{n}}^{T} X_{n} \right )^{-1} = \left ( (1, 2, 3, \cdots, n) \left ( \begin{matrix} 1 \\ 2 \\ \vdots \\ n \end{matrix} \right ) \right )^{-1} = \left ( 1 + 4 + \cdots + n^2 \right )^{-1} \to 0 \]

And:

\[ \max_{i=1,...,n} ( 1, 2, \cdots, n ) \left ( 1 + 4 + \cdots + n^2 \right )^{-1} \left ( \begin{matrix} 1 \\ 2 \\ \vdots \\ n \end{matrix} \right ) \to 0 \text{ for } n \to \infty \]

\(x_{i} = \frac{1}{i}\):

\[ \left ( {X_{n}}^{T} X_{n} \right )^{-1} = \left ( \left (1, 0.5, 0.33, \cdots, \frac{1}{n} \right ) \left ( \begin{matrix} 1 \\ 0.5 \\ \vdots \\ \frac{1}{n} \end{matrix} \right ) \right )^{-1} = \left ( \sum_{i=1}^{n} \frac{1}{i^{2}} \right )^{-1} \not{\to} 0 \]

And:

\[ \max_{i=1,...,n} \left ( 1, 0.5, \cdots, \frac{1}{n} \right ) \left ( \sum_{i = 1}^{n} \frac{1}{i^{2}} \right )^{-1} \left ( \begin{matrix} 1 \\ 0.5 \\ \vdots \\ \frac{1}{n} \end{matrix} \right ) \not{\to} 0 \text{ for } n \to \infty \]

\(x_{i} = \frac{1}{\sqrt{i}}\):

\[ \left ( {X_{n}}^{T} X_{n} \right )^{-1} = \left ( \sum_{i = 1}^{n} \frac{1}{i} \right )^{-1} \to 0 \]

And:

\[ \max_{i=1,...,n} \left ( 1, \frac{1}{\sqrt{2}}, \cdots, \frac{1}{\sqrt{n}} \right ) \left ( \sum_{i = 1}^{n} \frac{1}{i} \right )^{-1} \begin{matrix} 1 \\ \frac{1}{\sqrt{2}} \\ \vdots \\ \frac{1}{\sqrt{n}} \end{matrix} \to 0 \]

Example 3.12 Munich Rent Index — Hypothesis Testing

The regression model to adjust is:

\[ \begin{matrix} \text{rentsqm}_{i} = & \beta_{0} + \beta_{1} \text{areainvc}_{i} + \beta_{2} \text{yearco}_{i} + \beta_{3} \text{yearco2}_{i} + \beta_{4} \text{yearco3}_{i} \\ & + \beta_{5} \text{nkitchen}_{i} + \beta_{6} \text{pkitchen}_{i} + \beta_{7} \text{year01}_{i} + \epsilon_{i} \end{matrix} \]

hypothesis_model <- lm(
  rentsqm_euro ~ areainvc + yearco + yearco2 + yearco3 + nkitchen + pkitchen + year01,
  data = munich_rent_index_01
)
summary(hypothesis_model)

Call:
lm(formula = rentsqm_euro ~ areainvc + yearco + yearco2 + yearco3 + 
    nkitchen + pkitchen + year01, data = munich_rent_index_01)

Residuals:
   Min     1Q Median     3Q    Max 
-7.674 -1.270 -0.113  1.242 10.479 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)   6.93239    0.03757 184.539  < 2e-16 ***
areainvc    123.76987    4.42908  27.945  < 2e-16 ***
yearco       49.37274    2.02573  24.373  < 2e-16 ***
yearco2      29.49985    2.02515  14.567  < 2e-16 ***
yearco3      -0.88418    1.97229  -0.448  0.65396    
nkitchen      1.04340    0.10151  10.279  < 2e-16 ***
pkitchen      1.30205    0.15226   8.552  < 2e-16 ***
year01       -0.18524    0.06213  -2.981  0.00288 ** 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 1.964 on 4563 degrees of freedom
Multiple R-squared:  0.3385,    Adjusted R-squared:  0.3375 
F-statistic: 333.6 on 7 and 4563 DF,  p-value: < 2.2e-16

So we want to test the hypothesis:

\[ \begin{matrix} H_{0} : \beta_{7} = 0 & \text{against} & H_{1} : \beta_{7} \neq 0 \end{matrix} \]

About the significance of the follow-up measure.

\[ \begin{matrix} H_{0} : \left ( \begin{matrix} \beta_{5} \\ \beta_{6} \end{matrix} \right ) = \left ( \begin{matrix} 0 \\ 0 \end{matrix} \right ) & \text{against} & H_{1} : \left ( \begin{matrix} \beta_{5} \\ \beta_{6} \end{matrix} \right ) \neq \left ( \begin{matrix} 0 \\ 0 \end{matrix} \right ) \end{matrix} \]

About the significance of the kitchen variable. And a final test about the significance of differentiate between these two type of kitchens:

\[ \begin{matrix} H_{0} : \beta_{5} - \beta_{6} = 0 & \text{against} & H_{1} : \beta_{5} - \beta_{6} \neq 0 \end{matrix} \]

Example 3.13 Munich Rent Index — Standard Output and Hypothesis Tests

For the first test (\(H_{0} : \beta_{7} = 0\)), we calculate:

\[ t_{7} = \frac{\hat{\beta}_{7}}{{\widehat{\text{Var}(\hat{\beta}_{7})}}^{1/2}} \sim t_{1 - \alpha / 2} (n - p) \]

The covariance matrix is:

cov_matrix <- vcov(hypothesis_model)
cov_matrix
             (Intercept)     areainvc       yearco      yearco2      yearco3
(Intercept)  0.001411211  0.006823735  0.004991278  0.005173670 -0.002333528
areainvc     0.006823735 19.616785356 -1.294964483  1.420604414  0.122987111
yearco       0.004991278 -1.294964483  4.103591466  0.046451741 -0.006404590
yearco2      0.005173670  1.420604414  0.046451741  4.101229176  0.027782090
yearco3     -0.002333528  0.122987111 -0.006404590  0.027782090  3.889943976
nkitchen    -0.001093652 -0.091944737 -0.025946481 -0.026748293  0.006345756
pkitchen    -0.001128930  0.022989833 -0.042249319 -0.049085309 -0.016014007
year01      -0.001270640  0.004137400 -0.002253660 -0.001730023  0.007205398
                 nkitchen      pkitchen        year01
(Intercept) -1.093652e-03 -0.0011289298 -1.270640e-03
areainvc    -9.194474e-02  0.0229898326  4.137400e-03
yearco      -2.594648e-02 -0.0422493193 -2.253660e-03
yearco2     -2.674829e-02 -0.0490853093 -1.730023e-03
yearco3      6.345756e-03 -0.0160140070  7.205398e-03
nkitchen     1.030419e-02  0.0014042193  5.682974e-05
pkitchen     1.404219e-03  0.0231824417  1.902243e-04
year01       5.682974e-05  0.0001902243  3.860040e-03

We check the \(\text{Var}(\hat{\beta}_{7})\) is the last value.

var_7 <- cov_matrix[8, 8]
t_7 <- hypothesis_model$coefficients[8] / sqrt(var_7)
t_7
   year01 
-2.981496 

And this coincides with the value given in the summary.

For the second test, we compute the \(F\) value as:

\[ F = \frac{1}{r} {\hat{\beta}}^{T} \widehat{\text{Cov} \left ( \hat{\beta} \right )}^{-1} \hat{\beta} \sim F_{1, n - p} \]

With the \(\hat{\beta}\) define in the test \(\left ( \begin{matrix} \hat{\beta_{5}} \\ \hat{\beta_{6}} \end{matrix} \right )\), then \(r=2\) and \(n - p = n - 8\):

df <- nrow(munich_rent_index_01) - 8
r <- 2
beta_hat <- as.matrix(hypothesis_model$coefficients[6:7])
f_56 <- (1/2) * t(beta_hat) %*% solve(cov_matrix[6:7, 6:7]) %*% beta_hat
f_56
         [,1]
[1,] 82.08307

We get expected \(F\):

f_crit <- qf(p = 0.95, r, df)
f_crit
[1] 2.9977

And we reject the null hypothesis.

The third and final test comparing \(\beta_{5}\) and \(\beta_{6}\) between each other. We need:

\[ \widehat{\text{Var}(\hat{\beta}_{5} - \hat{\beta}_{6})} = \widehat{\text{Var}(\hat{\beta}_{5})} + \widehat{\text{Var}(\hat{\beta}_{6})} - 2 \cdot \widehat{\text{Cov}(\hat{\beta}_{5}, \hat{\beta}_{6})} \]

Using the \(\widehat{\text{Cov}(\hat{\beta})}\) matrix:

cov_5_6 <- cov_matrix[6, 7]
var_5 <- cov_matrix[6, 6]
var_6 <- cov_matrix[7, 7]
var_5_6 <- var_5 + var_6 - 2 * cov_5_6
f_5_6 <- (hypothesis_model$coefficients[6] - hypothesis_model$coefficients[7])^2 / var_5_6
f_5_6
nkitchen 
 2.18071 

And this \(F\) critical, using \(r=1\):

f_crit <- qf(p = 0.95, 1, df)
f_crit
[1] 3.843498

So, in this case, we do not reject the null hypothesis.

Example 3.14 Munich Rent Index — Confidence Intervals

The confidence interval for \(\beta_{7}\) is obtain using:

\[ \beta_{7} \pm t_{n - p} \left (1 - \alpha / 2 \right ) \cdot \widehat{\text{Var}(\hat{\beta}_{7})}^{1/2} \]

inter <- qt(1 - 0.05 / 2, df) * sqrt(var_7)
c(hypothesis_model$coefficients[8] - inter, hypothesis_model$coefficients[8] + inter)
    year01     year01 
-0.3070414 -0.0634347 

Example 3.15 Munich Rent Index — Prediction Intervals

Using the prediction interval:

\[ {x_{0}}^{T} \cdot \hat{\beta} \pm t_{n-p}(1 - \alpha / 2) \hat{\sigma} \left ( 1 + {x_{0}}^{T} \left (X^{T} X \right )^{1} x_{0} \right )^{1/2} \]

We can obtain the \(\hat{\sigma}\) directly from the model or using:

\[ (n - p) \hat{\sigma}^2 = \epsilon^{T} \epsilon \]

sigma_model <- summary(hypothesis_model)$sigma
sigma_2 <- t(as.matrix(hypothesis_model$residuals)) %*% as.matrix(hypothesis_model$residuals) / (nrow(munich_rent_index_01) - 8)
sigma <- sqrt(sigma_2)
c(sigma_model, sigma)
[1] 1.964112 1.964112

We are also going to use the design matrix:

design_matrix <- as.matrix(hypothesis_model$model)
design_matrix[, 1] <- 1
colnames(design_matrix)[1] <- "1"
head(design_matrix)
  1      areainvc       yearco      yearco2      yearco3 nkitchen pkitchen
1 1  0.0116672025 -0.011568466 -0.010460264  0.030118199        0        0
2 1 -0.0072887975 -0.011568466 -0.010460264  0.030118199        0        0
3 1  0.0175786025  0.009594692 -0.004320479 -0.013940551        0        0
4 1  0.0087368025  0.010256041 -0.003177207 -0.014569233        0        0
5 1 -0.0065948975  0.018853574  0.016932467 -0.005009151        0        0
6 1 -0.0007751975  0.003642554 -0.012015191 -0.002877678        0        0
  year01
1      0
2      0
3      0
4      0
5      0
6      0
area_seq <- seq(min(munich_rent_index_01$area), max(munich_rent_index_01$area), length.out = 100)
areainvc <- (1 / area_seq) - mean(1 / area_seq)

betam <- hypothesis_model$coefficients
yearcc <- munich_rent_index_01[munich_rent_index_01$yearc == 1918, ][1, ]
# Three last terms were added for completeness
constant <- betam[1] + betam[3] * yearcc$yearco + betam[4] * yearcc$yearco2 + betam[5] * yearcc$yearco3 + betam[6] * 0 + betam[7] * 0 + betam[8] * 0

plot(
  munich_rent_index_01$area,
  munich_rent_index_01$rentsqm_euro,
  xlab = 'area in sqm',
  ylab = 'estimated rent per sqm'
)
beta_1 <- hypothesis_model$coefficients[2]
lines(area_seq, constant + beta_1 * areainvc, col = 'red', lwd = 2)

conf_inter <- qt(1 - 0.05 / 2, df) * sqrt(cov_matrix[2, 2])
beta_conf <- c(beta_1 - conf_inter, beta_1 + conf_inter)
lines(area_seq, constant + beta_conf[1] * areainvc, col = 'darkblue', lwd = 2)
lines(area_seq, constant + beta_conf[2] * areainvc, col = 'darkblue', lwd = 2)

x_o <- as.matrix(cbind(
  1,
  areainvc,
  yearcc$yearco,
  yearcc$yearco2,
  yearcc$yearco3,
  0,
  0,
  0
))

pred_inter <- NULL

for (i in seq_len(nrow(x_o)))
  x_o_i <- as.matrix(x_o[i, ])
  pred_inter <- c(pred_inter, qt(1 - 0.05 / 2, df) * sigma * sqrt(
    1 + (
      t(x_o_i) %*% solve(t(design_matrix)%*%design_matrix) %*% x_o_i
   ))
)

lines(area_seq, constant + beta_conf[1] * areainvc - pred_inter, col = 'gray', lwd = 2)
lines(area_seq, constant + beta_conf[2] * areainvc + pred_inter, col = 'gray', lwd = 2)

This can also be done using predict:

x_o <- as.data.frame(cbind(
  areainvc,
  yearcc$yearco,
  yearcc$yearco2,
  yearcc$yearco3,
  0,
  0,
  0
))
colnames(x_o) <- colnames(hypothesis_model$model)[2:8]
confidence_interval <- predict(hypothesis_model, x_o, interval = "confidence")

plot(
    munich_rent_index_01$area,
    munich_rent_index_01$rentsqm_euro,
    xlab = 'area in sqm',
    ylab = 'estimated rent per sqm'
)
lines(area_seq, confidence_interval[, "fit"], col = 'red', lwd = 2)
lines(area_seq, confidence_interval[, "lwr"], col = 'darkblue', lwd = 2)
lines(area_seq, confidence_interval[, "upr"], col = 'darkblue', lwd = 2)

prediction_interval <- predict(hypothesis_model, x_o, interval = "prediction")
lines(area_seq, prediction_interval[, "lwr"], col = 'gray', lwd = 2)
lines(area_seq, prediction_interval[, "upr"], col = 'gray', lwd = 2)

Example 3.16 Polynomial Regression

Simulated data from the real model:

\[ y_{i} = -1 + 0.3 x_{i} + 0.4 {x_{i}}^{2} - 0.8 {x_{i}}^{3} + \epsilon_{i} \text{ with } \epsilon_{i} \sim N(0, {0.07}^{2}) \]

par(mfrow = c(3, 2))

x_seq <- seq(0, 1, length.out = 50)
training_data <- data.frame(
  x = x_seq,
  y = -1 + 0.3 * x_seq + 0.4 * x_seq ^ 2 - 0.8 * x_seq ^ 3 + rnorm(50, 0, 0.07)
)
validation_data <- data.frame(
  x = x_seq,
  y = -1 + 0.3 * x_seq + 0.4 * x_seq ^ 2 - 0.8 * x_seq ^ 3 + rnorm(50, 0, 0.07)
)

plot_training_data <- function(title) {
  plot(
    training_data,
    main = title,
    xlab = 'x',
    ylab = 'y'
  )
}

plot_training_data('training_data')
plot(
  validation_data,
  main = 'validation_data',
  xlab = 'x',
  ylab = 'y'
)

adjust_polynomial <- function(l) {
  model <- "y ~ x"
  if (l != 1) {
    for (i in 2:l){
      model <- paste0(model, " + I(x^", i, ")")
    }
  }
  lm(model, data = training_data)
}

polynomial_models <- lapply(1:9, adjust_polynomial)
plot_training_data('regression line')
lines(x_seq, predict(polynomial_models[1][[1]]), col = 'red', lwd = 2)

plot_training_data('polynomial regression with l=2')
lines(x_seq, predict(polynomial_models[2][[1]]), col = 'red', lwd = 2)

plot_training_data('polynomial regression with l=5')
lines(x_seq, predict(polynomial_models[5][[1]]), col = 'red', lwd = 2)

mean_square_error <- function(model, data){
  (1 / 50) * sum((data$y - predict(model, new_data = data)) ^ 2)
}

mse_training_data <- unlist(lapply(polynomial_models, mean_square_error, data = training_data))
mse_validation_data <- unlist(lapply(polynomial_models, mean_square_error, data = validation_data))

plot(
  seq(1, 9, 1),
  mse_training_data,
  ylim = c(min(mse_training_data, mse_validation_data), max(mse_training_data, mse_validation_data)),
  main = 'MSE for training and validation data',
  xlab = 'degree of polynomial',
  ylab = 'MSE',
  type = 'l',
  col = 'darkblue',
  lwd = 2
)
lines(seq(1, 9, 1), mse_validation_data, col = 'red', lwd = 2)

Example 3.17 Correlated Covariates

Simulate the data as following:

\[ x_{1} \sim U(0, 1) \wedge x_{3} \sim U(0, 1) \]

\[ x_{2} = x_{1} + u \text{ with } u \sim U(0, 1) \]

\[ y \sim N(-1 + 0.3 x_{1} + 0.2 x_{3}, {0.2}^{2}) \]

n <- 150

x1 <- runif(n, 0, 1)
x3 <- runif(n, 0, 1)
u <- runif(n, 0, 1)

x2 <- x1 + u

y <- rnorm(n, mean = -1 + 0.3 * x1 + 0.2 * x3, 0.2)

corr_cov_data <- data.frame(
  y = y,
  x1 = x1,
  x2 = x2,
  x3 = x3
)
pairs(corr_cov_data)

Adjust the model with \(x_{1}\), \(x_{2}\) and \(x_{3}\):

wrong_model <- lm(y ~ x1 + x2 + x3, data = corr_cov_data)
summary(wrong_model)

Call:
lm(formula = y ~ x1 + x2 + x3, data = corr_cov_data)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.45604 -0.12357  0.00926  0.11418  0.51568 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) -0.983404   0.049308 -19.944  < 2e-16 ***
x1           0.308749   0.077842   3.966 0.000114 ***
x2          -0.003877   0.054825  -0.071 0.943728    
x3           0.138695   0.051768   2.679 0.008228 ** 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.1858 on 146 degrees of freedom
Multiple R-squared:  0.2057,    Adjusted R-squared:  0.1894 
F-statistic:  12.6 on 3 and 146 DF,  p-value: 2.252e-07

Adjust the model with \(x_{1}\) and \(x_{3}\):

correct_model <- lm(y ~ x1 + x3, data = corr_cov_data)
summary(correct_model)

Call:
lm(formula = y ~ x1 + x3, data = corr_cov_data)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.45722 -0.12438  0.00888  0.11348  0.51427 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) -0.98526    0.04160 -23.685  < 2e-16 ***
x1           0.30469    0.05242   5.812 3.69e-08 ***
x3           0.13868    0.05159   2.688  0.00802 ** 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.1852 on 147 degrees of freedom
Multiple R-squared:  0.2057,    Adjusted R-squared:  0.1949 
F-statistic: 19.03 on 2 and 147 DF,  p-value: 4.465e-08

Example 3.18 Polynomial Regression — Model Choice with AIC

We calculate AIC using:

\[ AIC = n \cdot \log(\hat{\sigma}^{2}) + 2 \left ( \left | M \right | + 1 \right ) \]

aic <- function(model){
  m <- ncol(model$model) - 1
  n <- nrow(model$model)
  n * log(summary(model)$sigma^2) + 2 * (m + 1)
}

aic_values <- unlist(lapply(polynomial_models, aic))
plot(
  aic_values,
  main = 'AIC as a function of degree of polynomials',
  xlab = 'degrees of polynomial',
  ylab = 'aic',
  type = 'l',
  col = 'darkblue',
  lwd = 2
)

As always, this can also be done using R:

aic <- function(model){
  AIC(model)
}

aic_values <- unlist(lapply(polynomial_models, aic))
plot(
        aic_values,
        main = 'AIC as a function of degree of polynomials',
        xlab = 'degrees of polynomial',
        ylab = 'aic',
        type = 'l',
        col = 'darkblue',
        lwd = 2
)

Example 3.19 Prices of Used Cars — Model Choice

As for all dataset, we import the “prices of used cars” dataset from the official page of the dataset:

cars_url <- "https://www.uni-goettingen.de/de/document/download/062cadfda1c4c295f9460b49c7f5799e.raw/golffull.raw"

used_cars <- read.table(
  url(cars_url),
  header = 1,
  colClasses = c(
    "numeric", "integer", "numeric",
    "integer", "factor", "factor",
    "numeric", "numeric", "numeric",
    "numeric", "numeric", "numeric"
  )
)

# Convert to logical
used_cars$extras1 <- used_cars$extras1 == 1
used_cars$extras2 <- used_cars$extras2 == 1

# Recalculate polynomials
poly_kilometer <- poly(used_cars$kilometer, 3)
used_cars[, c("kilometerop1", "kilometerop2", "kilometerop3")] <- poly_kilometer
poly_age <- poly(used_cars$age, 3)
used_cars[, c("ageop1", "ageop2", "ageop3")] <- poly_age
summary(used_cars)
     price            age           kilometer          TIA       
 Min.   :1.450   Min.   : 65.00   Min.   : 10.0   Min.   : 0.00  
 1st Qu.:2.450   1st Qu.: 99.75   1st Qu.:107.0   1st Qu.:11.00  
 Median :3.100   Median :115.00   Median :130.8   Median :19.00  
 Mean   :3.397   Mean   :113.19   Mean   :134.7   Mean   :16.98  
 3rd Qu.:3.960   3rd Qu.:129.00   3rd Qu.:167.5   3rd Qu.:24.00  
 Max.   :7.300   Max.   :142.00   Max.   :250.0   Max.   :26.00  
  extras1         extras2         kilometerop1        kilometerop2     
 Mode :logical   Mode :logical   Min.   :-0.213325   Min.   :-0.05651  
 FALSE:54        FALSE:43        1st Qu.:-0.047433   1st Qu.:-0.04987  
 TRUE :118       TRUE :129       Median :-0.006815   Median :-0.03005  
                                 Mean   : 0.000000   Mean   : 0.00000  
                                 3rd Qu.: 0.056036   3rd Qu.: 0.02175  
                                 Max.   : 0.197130   Max.   : 0.40437  
  kilometerop3           ageop1              ageop2             ageop3        
 Min.   :-0.532904   Min.   :-0.193852   Min.   :-0.07929   Min.   :-0.43373  
 1st Qu.:-0.047845   1st Qu.:-0.054070   1st Qu.:-0.06335   1st Qu.:-0.05421  
 Median : 0.009036   Median : 0.007273   Median :-0.02235   Median :-0.01263  
 Mean   : 0.000000   Mean   : 0.000000   Mean   : 0.00000   Mean   : 0.00000  
 3rd Qu.: 0.045786   3rd Qu.: 0.063588   3rd Qu.: 0.04385   3rd Qu.: 0.06266  
 Max.   : 0.342683   Max.   : 0.115881   Max.   : 0.32072   Max.   : 0.18010  

Exploring the data:

par(mfrow = c(3, 2))

plot(
  used_cars$age,
  used_cars$price,
  main = 'Scatter plot: sales price versus age in months',
  xlab = 'age in months',
  ylab = 'sales prince in 1000 Euro'
)

plot(
  used_cars$kilometer,
  used_cars$price,
  main = 'Scatter plot: sales price versus kilometer reading',
  xlab = 'kilometer reading in 1000 km',
  ylab = 'sales prince in 1000 Euro'
)

plot(
  used_cars$TIA,
  used_cars$price,
  main = 'Scatter plot: sales price versus months until TIA',
  xlab = 'months until next TIA appointment',
  ylab = 'sales prince in 1000 Euro'
)

boxplot(
  price ~ extras1,
  data = used_cars,
  main = 'Box plot: sales prince with or without ABS',
  xlab = '',
  ylab = 'sales prices in 1000 Euro',
  names = c('no ABS', 'ABS')
)

boxplot(
  price ~ extras2,
  data = used_cars,
  main = 'Box plot: sales prince with or without sunroof',
  xlab = '',
  ylab = 'sales prices in 1000 Euro',
  names = c('no sunroof', 'sunroof')
)

We explore the models suggested:

\(M1\)

\[ \begin{matrix} \mathbb{E}(\text{price}) = & \beta_{0} + \beta_{1} \text{kilometerop1} + \beta_{2} \text{kilometerop2} + \beta_{3} \text{kilometerop3} \\ & + \beta_{4} \text{ageop1} + \beta_{5} \text{ageop2} + \beta_{6} \text{ageop3} \end{matrix} \]

With \(\text{kilometerop}i\) the \(i\) term of a polynomial of degree 3 of the \(\text{kilometer}\) variable. Equivalent to \(\text{ageop}i\).

\(M2\)

\[ \begin{matrix} \mathbb{E}(\text{price}) = & \beta_{0} + \beta_{1} \text{kilometerop1} + \beta_{2} \text{kilometerop2} + \beta_{3} \text{kilometerop3} \\ & + \beta_{4} \text{ageop1} + \beta_{5} \text{ageop2} + \beta_{6} \text{ageop3} \\ & + \beta_{7} \text{extras1} \end{matrix} \]

\(M3\)

\[ \begin{matrix} \mathbb{E}(\text{price}) = & \beta_{0} + \beta_{1} \text{kilometerop1} + \beta_{2} \text{kilometerop2} + \beta_{3} \text{kilometerop3} \\ & + \beta_{4} \text{ageop1} + \beta_{5} \text{ageop2} + \beta_{6} \text{ageop3} \\ & + \beta_{7} \text{extras2} \end{matrix} \]

\(M4\)

\[ \begin{matrix} \mathbb{E}(\text{price}) = & \beta_{0} + \beta_{1} \text{kilometerop1} + \beta_{2} \text{kilometerop2} + \beta_{3} \text{kilometerop3} \\ & + \beta_{4} \text{ageop1} + \beta_{5} \text{ageop2} + \beta_{6} \text{ageop3} \\ & + \beta_{7} \text{TIA} \end{matrix} \]

\(M5\)

\[ \begin{matrix} \mathbb{E}(\text{price}) = & \beta_{0} + \beta_{1} \text{kilometerop1} + \beta_{2} \text{kilometerop2} + \beta_{3} \text{kilometerop3} \\ & + \beta_{4} \text{ageop1} + \beta_{5} \text{ageop2} + \beta_{6} \text{ageop3} \\ & + \beta_{7} \text{extras1} + \beta_{8} \text{extras2} \end{matrix} \]

\(M6\)

\[ \begin{matrix} \mathbb{E}(\text{price}) = & \beta_{0} + \beta_{1} \text{kilometerop1} + \beta_{2} \text{kilometerop2} + \beta_{3} \text{kilometerop3} \\ & + \beta_{4} \text{ageop1} + \beta_{5} \text{ageop2} + \beta_{6} \text{ageop3} \\ & + \beta_{7} \text{extras1} + \beta_{8} \text{TIA} \end{matrix} \]

\(M7\)

\[ \begin{matrix} \mathbb{E}(\text{price}) = & \beta_{0} + \beta_{1} \text{kilometerop1} + \beta_{2} \text{kilometerop2} + \beta_{3} \text{kilometerop3} \\ & + \beta_{4} \text{ageop1} + \beta_{5} \text{ageop2} + \beta_{6} \text{ageop3} \\ & + \beta_{7} \text{extras2} + \beta_{8} \text{TIA} \end{matrix} \]

\(M8\)

\[ \begin{matrix} \mathbb{E}(\text{price}) = & \beta_{0} + \beta_{1} \text{kilometerop1} + \beta_{2} \text{kilometerop2} + \beta_{3} \text{kilometerop3} \\ & + \beta_{4} \text{ageop1} + \beta_{5} \text{ageop2} + \beta_{6} \text{ageop3} \\ & + \beta_{7} \text{extras1} + \beta_{8} \text{extras2} + \beta_{9} \text{TIA} \end{matrix} \]

base_car_model <- "price ~ kilometerop1 + kilometerop2 + kilometerop3 + ageop1 + ageop2 + ageop3"
used_cars_model <- list(lm(base_car_model, data = used_cars))
extra_var <- c("extras1", "extras2", "TIA")
for (extra in extra_var){
  new_model <- paste0(base_car_model, " + ", extra)
  used_cars_model[[length(used_cars_model) + 1]] <- lm(new_model, data = used_cars)
}
for (tuple in combn(extra_var, 2, simplify = FALSE)){
  new_model <- paste0(base_car_model, " + ", tuple[1], " + ", tuple[2])
  used_cars_model[[length(used_cars_model) + 1]] <- lm(new_model, data = used_cars)
}
all_extras <- paste(extra_var, collapse = " + ")
used_cars_model[[length(used_cars_model) + 1]] <- lm(paste0(base_car_model, " + ", all_extras), data = used_cars)

cars_aic <- data.frame(model = seq(1, 8), aic = unlist(lapply(used_cars_model, AIC)))
cars_aic

9th model are obtained using “leaps and bounds” algorithm, already implemented on R:

library(leaps)

leaps_model <- regsubsets(price ~ . -age -kilometer, data = used_cars, nvmax = 4)

summary(leaps_model)
Subset selection object
Call: regsubsets.formula(price ~ . - age - kilometer, data = used_cars, 
    nvmax = 4)
9 Variables  (and intercept)
             Forced in Forced out
TIA              FALSE      FALSE
extras1TRUE      FALSE      FALSE
extras2TRUE      FALSE      FALSE
kilometerop1     FALSE      FALSE
kilometerop2     FALSE      FALSE
kilometerop3     FALSE      FALSE
ageop1           FALSE      FALSE
ageop2           FALSE      FALSE
ageop3           FALSE      FALSE
1 subsets of each size up to 4
Selection Algorithm: exhaustive
         TIA extras1TRUE extras2TRUE kilometerop1 kilometerop2 kilometerop3
1  ( 1 ) " " " "         " "         " "          " "          " "         
2  ( 1 ) " " " "         " "         "*"          " "          " "         
3  ( 1 ) " " " "         " "         "*"          " "          " "         
4  ( 1 ) " " " "         " "         "*"          "*"          " "         
         ageop1 ageop2 ageop3
1  ( 1 ) "*"    " "    " "   
2  ( 1 ) "*"    " "    " "   
3  ( 1 ) "*"    "*"    " "   
4  ( 1 ) "*"    "*"    " "   
 
 
 
 
 
 
 
 
 
 
 
 
 
*
*
*
 
 
 
*
 
 
 
 
*
*
*
*
 
 
*
*
 
 
 
 

As in the book the resulted model is:

\(M9\)

\[ \mathbb{E}(\text{price}) = \beta_{0} + \beta_{1} \text{kilometerop1} + \beta_{2} \text{kilometerop2} + \beta_{3} \text{ageop1} + \beta_{4} \text{ageop2} \]

We add it to the table and plot the AIC:

selected_var <- names(which(summary(leaps_model)$outmat[4, ] == "*"))
used_cars_model[[length(used_cars_model) + 1 ]] <- lm(
  paste0("price ~ ", paste(selected_var, collapse= " + ")), data = used_cars
)
cars_aic[9, ] <- c(9, AIC(used_cars_model[[9]]))
cars_aic
plot(
  cars_aic,
  main = 'AIC for the different models',
  xlab = 'model number',
  ylab = 'AIC',
  xaxt = 'n'
)
axis(1, at = 1:9)

Showing the age and kilometer effects as in Example 3.5.

par(mfrow = c(1, 2))

beta_m9 <- used_cars_model[[9]]$coefficients

age_seq <- seq(min(used_cars$age), max(used_cars$age), length.out = 100)
kilometer_seq <- seq(min(used_cars$kilometer), max(used_cars$kilometer), length.out = 100)

age_effect <- function(age){
  i_poly_age <- predict(poly_age, age)
  beta_m9[4] * i_poly_age[, 1] + beta_m9[5] * i_poly_age[, 2]
}

kilometer_effect <- function(kilometer){
  i_poly_killometer <- predict(poly_kilometer, kilometer)
  beta_m9[2] * i_poly_killometer[, 1] + beta_m9[3] * i_poly_killometer[, 2]
}

residual_age <- used_cars$price - beta_m9[1] - kilometer_effect(used_cars$kilometer)
plot(
  used_cars$age,
  residual_age,
  main = 'effect of age including partial residuals',
  xlab = 'age in months',
  ylab = 'effect / partial residuals'
)
lines(age_seq, age_effect(age_seq), col = 'red', lwd = 2)

residual_kilometer <- used_cars$price - beta_m9[1] - age_effect(used_cars$age)
plot(
  used_cars$kilometer,
  residual_age,
  main = 'effect of kilometer reading including partial residuals',
  xlab = 'kilometer reading in 1000km',
  ylab = 'effect / partial residuals'
)
lines(kilometer_seq, kilometer_effect(kilometer_seq), col = 'red', lwd = 2)

Example 3.20 Price of Used Cars — Model Diagnosis

The studentized residuals are calculated with the equation:

\[ r_{i}^{*} = \frac{\hat{\epsilon}_{i}}{\hat{\sigma}_{(i)} \sqrt{1 - h_{ii}}} \]

But, you can get them using R:

par(mfrow = c(2, 2))

t_residuals <- rstudent(used_cars_model[[9]])

n <- nrow(used_cars_model[[9]]$model)
p <- ncol(used_cars_model[[9]]$model)
t_critical <- qt(1 - 0.05 / (2 * n), n - p - 1)

plot_studentized_residuals <- function(against, ag_label, short_label){
  plot(
    against,
    t_residuals,
    main = paste0('studentized residuals verus ', short_label),
    xlab = ag_label,
    ylab = 'studentized residuals',
    ylim = c(min(c(-t_critical - 0.5, t_residuals)), max(c(t_critical + 0.5, t_residuals)))
  )
  abline(h = 0, col = 'red', lwd = 2)
  abline(h = c(-t_critical, t_critical), col = 'gray', lwd = 2)
}
plot_studentized_residuals(used_cars_model[[9]]$fitted.values, 'estimated sales price', 'estimated sales price')
plot_studentized_residuals(used_cars$kilometer, 'kilometer reading in 1000km', 'kilometer reading')
plot_studentized_residuals(used_cars$age, 'age in months', 'age in months')
plot_studentized_residuals(used_cars$TIA, 'months to the next TIA appointment', 'months to TIA')

For the standardized residuals:

\[ r_{i} = \frac{\hat{\epsilon}_{i}}{\hat{\sigma}\sqrt{1 - h_{ii}}} \]

But using R:

n_residuals <- rstandard(used_cars_model[[9]])

qqnorm(n_residuals, col = 'darkblue', main = '', xlab = 'quantiles of normal distribution', ylab = 'quantiles of residuals')
abline(0, 1, col = 'black', lwd = 2)

Example 3.21 Prices of Used Cars — Collinearity

Now, we want the \(VIF\):

\[ {VIF}_{j} = \frac{1}{1 - {R_{j}}^{2}} \]

library(car)
Loading required package: carData
vif(used_cars_model[[9]])
kilometerop1 kilometerop2       ageop1       ageop2 
    1.179534     1.033982     1.187743     1.025773 

All values \(< 10\).

Example 3.22 Prices of Used Cars—Outliers

See graph of example 3.20. Finding the outlier:

t_residuals[t_residuals > t_critical]
      35 
4.364175 

Example 3.23 Prices of Used Cars — Influence Analysis

Getting the \(h_{ii}\) values:

\[ h_{ii} = \frac{1}{n} + {\left ( x_{i} - \overline{x} \right )}^{T} \left ( {\widetilde{X}_{j}}^{T} \widetilde{X}_{j} \right )^{-1} \left ( x_{i} - \overline{x} \right ) \]

leverage <- hatvalues(used_cars_model[[9]])
head(leverage)
         1          2          3          4          5          6 
0.23265338 0.13750066 0.09221031 0.05589964 0.04240741 0.07463096 
range(leverage)
[1] 0.01066563 0.23265338
h_critical <- 2 * p / n

big_leverage <- leverage > h_critical
leverage[big_leverage]
         1          2          3          6         43         53         62 
0.23265338 0.13750066 0.09221031 0.07463096 0.15735648 0.07461053 0.08509555 
       167        169        170        171        172 
0.10340534 0.07523947 0.10032390 0.14468732 0.13843795 

We can plot these points:

par(mfrow = c(1, 2))

plot(
  used_cars$age,
  used_cars$price,
  main = 'Scatter plot: sales price versus age in months',
  xlab = 'age in months',
  ylab = 'sales prince in 1000 Euro'
)
points(used_cars$age[big_leverage], used_cars$price[big_leverage], col = 'red', pch = 16)

plot(
  used_cars$kilometer,
  used_cars$price,
  main = 'Scatter plot: sales price versus kilometer reading',
  xlab = 'kilometer reading in 1000 km',
  ylab = 'sales prince in 1000 Euro'
)
points(used_cars$kilometer[big_leverage], used_cars$price[big_leverage], col = 'red', pch = 16)

And the Cook’s Distance:

\[ D_{i} = \frac{{\left ( \hat{y}_{(i)} - \hat{y} \right )}^{T} \left ( \hat{y}_{(i)} - \hat{y} \right )}{p \cdot {\hat{\sigma}}^{2}} \]

cook <- cooks.distance(used_cars_model[[9]])
head(cook)
           1            2            3            4            5            6 
0.0001119627 0.0474394511 0.0566528215 0.0008099303 0.0087695413 0.0045889281 
range(cook)
[1] 1.041578e-07 1.171480e-01
big_cook <- cook > 0.5
cook[big_cook]
named numeric(0)

Example 3.24 Prices of Used Cars — Alternative Models

Adjust model 9 for all observations, for \(\text{kilometer} \leq 230\) and \(\text{kilometer} \geq 50\):

model_230 <- lm(price ~ kilometerop1 + kilometerop2 + ageop1 + ageop2, data = used_cars[used_cars$kilometer <= 230, ])
model_50 <- lm(price ~ kilometerop1 + kilometerop2 + ageop1 + ageop2, data = used_cars[used_cars$kilometer >= 50, ])

new_kilometer_effect <- function(kilometer, beta){
  i_poly_killometer <- predict(poly_kilometer, kilometer)
  beta[2] * i_poly_killometer[, 1] + beta[3] * i_poly_killometer[, 2]
}

plot(
  kilometer_seq,
  kilometer_effect(kilometer_seq),
  main = '',
  xlab = 'kilometer reading in 1000 km',
  ylab = 'effects',
  type = 'l',
  lwd = 2
)
lines(kilometer_seq, new_kilometer_effect(kilometer_seq, model_230$coefficients), col = 'red', lwd = 2)
lines(kilometer_seq, new_kilometer_effect(kilometer_seq, model_50$coefficients), col = 'green', lwd = 2)

legend(
  "topright",
  legend = c('LS with all observation', 'LS without kilometer>230', 'LS without kilometer<50'),
  col = c('black', 'red', 'green'),
  lwd = 2
)

LS0tCnRpdGxlOiAiQ2xhc3NpY2FsIExpbmVhciBNb2RlbCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CnNldC5zZWVkKDEyMykKYGBgCgojIyBIb21vc2NlZGFzdGljIEVycm9yIFZhcmlhbmNlcwoKRm9yIHRoZSB2aXN1YWxpemF0aW9uIG9mIGhvbW9jZWRhc3RpYyBhbmQgaGV0ZXJjZWRhc3RpYyB2YXJpYW5jZSwgd2Ugc2ltdWxhdGU6CgokJAp5IFxzaW0gTiBcbGVmdCAoIC0xICsgMngsIDEgXHJpZ2h0ICkKJCQKClRoZSBmdW5uZWwtc2hhcGVkIGhldGVyb2NlZGFzdGljeSBpcyBzaW11bGF0ZWQgdXNpbmc6CgokJAp5IFxzaW0gTiBcbGVmdCAoIC0xICsgMngsIFxsZWZ0ICggMC4xICsgMC4zIFxsZWZ0ICggeCArIDMgXHJpZ2h0ICkgXHJpZ2h0ICleezJ9IFxyaWdodCApCiQkCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTgsIGZpZy5hbGlnbj0nY2VudGVyJ30KcGFyKG1mcm93ID0gYygyLCAyKSkKCiMgSG9tZWNlZGFzdGljIHZhcmlhbmNlClggPC0gc2VxKC0yLCAyLCBsZW5ndGgub3V0ID0gMzAwKQoKaG9tZWNlZGFzdGljX3JhbmRvbSA8LSBmdW5jdGlvbiAoeCl7CiAgcm5vcm0oMSwgbWVhbiA9IC0xICsgMiAqIHgsICBzZCA9IDEpWzFdCn0KCmhvbW9feSA8LSBzYXBwbHkoWCwgaG9tZWNlZGFzdGljX3JhbmRvbSkKaG9tb19lIDwtICgtMSArIDIgKiBYKSAtIGhvbW9feQoKcGxvdChYLCBob21vX3ksIHhsYWI9JycsIHlsYWI9JycsIG1haW49J2hvbW9jZWRhc3RpYyB2YXJpYW5jZScpCmFibGluZSgtMSwgMiwgY29sID0gInJlZCIsIGx3ZD0yKQpwbG90KFgsIGhvbW9fZSwgeGxhYj0nJywgeWxhYj0nJywgbWFpbj0naG9tb2NlZGFzdGljIHZhcmlhbmNlIGVycm9ycycpCmFibGluZSgwLCAwLCBjb2wgPSAicmVkIiwgbHdkPTIpCgpoZXRlcm9jZWRhc3RpY19yYW5kb20gPC0gZnVuY3Rpb24gKHgpewogIHJub3JtKDEsIG1lYW4gPSAtMSArIDIgKiB4LCBzZCA9IDAuMSArIDAuMyAqICh4ICsgMykpWzFdCn0KCmhldGVyb195IDwtIHNhcHBseShYLCBoZXRlcm9jZWRhc3RpY19yYW5kb20pCmhldGVyb19lIDwtICgtMSArIDIgKiBYKSAtIGhldGVyb195CgpwbG90KFgsIGhldGVyb195LCB4bGFiPScnLCB5bGFiPScnLCBtYWluPSdmdW5uZWwtc2hhcGVkIGhldGVyb2NlZGFzdGljIHZhcmlhbmNlJykKYWJsaW5lKC0xLCAyLCBjb2wgPSAicmVkIiwgbHdkPTIpCnBsb3QoWCwgaGV0ZXJvX2UsIHhsYWI9JycsIHlsYWI9JycsIG1haW49J2Z1bm5lbC1zaGFwZWQgaGV0ZXJvY2VkYXN0aWMgdmFyaWFuY2UgZXJyb3JzJykKYWJsaW5lKDAsIDAsIGNvbCA9ICJyZWQiLCBsd2Q9MikKYGBgCgojIyBFeGFtcGxlIDMuMSBNdW5pY2ggUmVudCBJbmRleOKAlEhldGVyb3NjZWRhc3RpYyBWYXJpYW5jZXMKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSdjZW50ZXInfQpwYXIobWZyb3cgPSBjKDEsIDIpKQoKbXVuaWNoX3JlbnRfdXJsIDwtICJodHRwczovL3d3dy51bmktZ29ldHRpbmdlbi5kZS9kZS9kb2N1bWVudC9kb3dubG9hZC82NGMyOWMxYjFmY2NiMTQyY2ZhOGYyOWE5NDJhOWUwNS5yYXcvcmVudDk5LnJhdyIKCm11bmljaF9yZW50X2luZGV4IDwtIHJlYWQudGFibGUoCiAgdXJsKG11bmljaF9yZW50X3VybCksCiAgaGVhZGVyID0gMSwKICBjb2xDbGFzc2VzID0gYygKICAgICJudW1lcmljIiwgIm51bWVyaWMiLCAibnVtZXJpYyIsCiAgICAibnVtZXJpYyIsICJmYWN0b3IiLCAiZmFjdG9yIiwKICAgICJmYWN0b3IiLCAiZmFjdG9yIiwgImZhY3RvciIKICApCikKCnNpbXBsZV9yZWcgPC0gbG0ocmVudCB+IGFyZWEsIGRhdGEgPSBtdW5pY2hfcmVudF9pbmRleCkKCnBsb3QobXVuaWNoX3JlbnRfaW5kZXgkYXJlYSwgbXVuaWNoX3JlbnRfaW5kZXgkcmVudCwgeGxhYiA9ICdhcmVhIGluIHNxbScsIHlsYWIgPSAnbmV0IHJlbnQgaW4gRXVybycpCmFibGluZShzaW1wbGVfcmVnLCBjb2wgPSAicmVkIiwgbHdkID0gMikKCnByZWRpY3RlZCA8LSBwcmVkaWN0KHNpbXBsZV9yZWcpCgpwbG90KG11bmljaF9yZW50X2luZGV4JGFyZWEsIHByZWRpY3RlZCAtIG11bmljaF9yZW50X2luZGV4JHJlbnQsIHhsYWIgPSAnYXJlYSBpbiBzcW0nLCB5bGFiID0gJ3Jlc2lkdWFscycpCmFibGluZSgwLCAwLCBjb2wgPSAicmVkIiwgbHdkID0gMikKYGBgCgojIyBVbmNvcnJlbGF0ZWQgRXJyb3JzCgpTaW11bGF0ZWQgYXV0b2NvcnJlbGF0ZWQgZXJyb3JzIHdpdGggcG9zaXRpdmUgY29ycmVsYXRpb24gYXJlIGdlbmVyYXRlZCB1c2luZzoKCiQkCnlfe2l9ID0gLTEgKyAyIHhfe2l9ICsgXGVwc2lsb25fe2l9CiQkCiQkClx0ZXh0e1doZXJlIH0gXGVwc2lsb25fe2l9ID0gMC45IFxlcHNpbG9uX3tpIC0gMX0gKyBcbXVfe2l9IFx3ZWRnZSBcbXVfe2l9IFxzaW0gTigwLCAwLjVeezJ9KQokJAoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD00LCBmaWcuYWxpZ249J2NlbnRlcid9CnBhcihtZnJvdyA9IGMoMSwgMikpCgpuIDwtIDEwMApYIDwtIHNlcSgtMywgMywgbGVuZ3RoLm91dCA9IG4pCm11IDwtIHJub3JtKG4sIG1lYW4gPSAwLCBzZCA9IDAuNSkKCmVwc2lsb24gPC0gbXVbMV0KZm9yKGkgaW4gc2VxKDIsIG4pKXsKICBlcHNpbG9uIDwtIGMoZXBzaWxvbiwgMC45ICogZXBzaWxvbltpIC0gMV0gKyBtdVtpXSkKfQoKeV9wcmVkIDwtIC0xICsgMiAqIFggKyBlcHNpbG9uCgpwbG90KFgsIHlfcHJlZCwgeGxhYiA9ICd4JywgeWxhYiA9ICcnLCBtYWluID0gJ29ic2VydmF0aW9ucyBhbmQgcmVncmVzc2lvbiBsaW5lJykKYWJsaW5lKC0xLCAyLCBjb2wgPSAicmVkIiwgbHdkID0gMikKCnBsb3QoZXBzaWxvbiwgeGxhYiA9ICdpJywgeWxhYiA9ICcnLCBtYWluID0gJ3RpbWUgc2VyaWVzIG9mIHRoZSBlcnJvcnMnKQphYmxpbmUoMCwgMCwgY29sID0gInJlZCIsIGx3ZCA9IDIpCmBgYAoKU2ltdWxhdGVkIGF1dG9jb3JyZWxhdGVkIGVycm9ycyB3aXRoIG5lZ2F0aXZlIGNvcnJlbGF0aW9uIGFyZSBnZW5lcmF0ZWQgdXNpbmc6CgokJAp5X3tpfSA9IC0xICsgMiB4X3tpfSArIFxlcHNpbG9uX3tpfQokJAokJApcdGV4dHtXaGVyZSB9IFxlcHNpbG9uX3tpfSA9IC0gMC45IFxlcHNpbG9uX3tpIC0gMX0gKyBcbXVfe2l9IFx3ZWRnZSBcbXVfe2l9IFxzaW0gTigwLCAwLjVeezJ9KQokJAoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD00LCBmaWcuYWxpZ249J2NlbnRlcid9CnBhcihtZnJvdyA9IGMoMSwgMikpCgpuIDwtIDEwMApYIDwtIHNlcSgtMywgMywgbGVuZ3RoLm91dCA9IG4pCm11IDwtIHJub3JtKG4sIG1lYW4gPSAwLCBzZCA9IDAuNSkKCmVwc2lsb24gPC0gbXVbMV0KZm9yKGkgaW4gc2VxKDIsIG4pKXsKICBlcHNpbG9uIDwtIGMoZXBzaWxvbiwgLTAuOSAqIGVwc2lsb25baSAtIDFdICsgbXVbaV0pCn0KCnlfcHJlZCA8LSAtMSArIDIgKiBYICsgZXBzaWxvbgoKcGxvdChYLCB5X3ByZWQsIHhsYWIgPSAneCcsIHlsYWIgPSAnJywgbWFpbiA9ICdvYnNlcnZhdGlvbnMgYW5kIHJlZ3Jlc3Npb24gbGluZScpCmFibGluZSgtMSwgMiwgY29sID0gInJlZCIsIGx3ZCA9IDIpCgpwbG90KGVwc2lsb24sIHhsYWIgPSAnaScsIHlsYWIgPSAnJywgbWFpbiA9ICd0aW1lIHNlcmllcyBvZiB0aGUgZXJyb3JzJykKYWJsaW5lKDAsIDAsIGNvbCA9ICJyZWQiLCBsd2QgPSAyKQpgYGAKCk1pc3BlY2lmaWVkIG1vZGVsIGlzIHNpbXVsYXRlZCB1c2luZzoKCiQkCnlfe2l9ID0gXHNpbih4X3tpfSkgKyB4X3tpfSArIFxlcHNpbG9uX3tpfQokJAokJApcdGV4dHtXaGVyZSB9IFxlcHNpbG9uX3tpfSBcc2ltIE4oMCwgMC4zXnsyfSkKJCQKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9OCwgZmlnLmFsaWduPSdjZW50ZXInfQpwYXIobWZyb3cgPSBjKDIsIDIpKQoKbiA8LSAxMDAKWCA8LSBzZXEoLTMsIDMsIGxlbmd0aC5vdXQgPSBuKQplcHNpbG9uIDwtIHJub3JtKG4sIG1lYW4gPSAwLCBzZCA9IDAuMykKCnkgPC0gc2luKFgpICsgWCArIGVwc2lsb24KCnBsb3QoWCwgeSwgeGxhYiA9ICcnLCB5bGFiID0gJycsIG1haW4gPSAnb2JzZXJ2YXRpb25zIGFuZCB0cnVlIGZ1bmN0aW9uJykKbGluZXMoWCwgc2luKFgpICsgWCwgY29sID0gInJlZCIsIGx3ZCA9IDIpCgpsaW5lYXJfbW9kZWwgPC0gbG0oeSB+IFgpCgpwbG90KFgsIHksIHhsYWIgPSAnJywgeWxhYiA9ICcnLCBtYWluID0gJ29ic2VydmF0aW9ucyBhbmQgcmVncmVzc2lvbiBsaW5lJykKYWJsaW5lKGxpbmVhcl9tb2RlbCwgY29sID0gInJlZCIsIGx3ZCA9IDIpCgp5X3ByZWQgPC0gcHJlZGljdChsaW5lYXJfbW9kZWwsIGRhdGEgPSBYKQpyZXMgPC0geV9wcmVkIC0geQpwbG90KFgsIHJlcywgeGxhYiA9ICcnLCB5bGFiID0gJycsIG1haW4gPSAncmVzaWR1YWxzJykKYWJsaW5lKDAsIDAsIGNvbCA9ICJyZWQiLCBsd2QgPSAyKQpgYGAKCiMjIEFkZGl0aXZpdHkgb2YgRXJyb3JzCgpXZSBzaW11bGF0ZSBkYXRhIHVzaW5nOgoKJCQKeV97aX0gPSBcZXhwKDEgKyB4X3tpMX0gLSB4X3tpMn0gKyBcZXBzaWxvbl97aX0pCiQkCgpXaXRoOgokJApcZXBzaWxvbl97aX0gXHNpbSBOIFxsZWZ0ICggMCwgezAuNH1eezJ9IFxyaWdodCApCiQkCgpXZSBwbG90IHRoaXMgbW9kZWw6CgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTgsIGZpZy5hbGlnbj0nY2VudGVyJ30KcGFyKG1mcm93ID0gYygyLCAyKSkKCm4gPC0gNTAKCngxIDwtIHNlcSgwLCAzLCBsZW5ndGgub3V0ID0gbikKeDIgPC0gc2VxKDAsIDMsIGxlbmd0aC5vdXQgPSBuKQp4X2dyaWQgPC0gZXhwYW5kLmdyaWQoeDEgPSB4MSwgeDIgPSB4MikKZXBzaWxvbiA8LSBybm9ybShuXjIsIG1lYW4gPSAwLCBzZCA9IDAuNCkKeSA8LSBleHAoMSArIHhfZ3JpZCR4MSAtIHhfZ3JpZCR4MiArIGVwc2lsb24pCgpwbG90KAogIHhfZ3JpZCR4MSwgeSwKICBtYWluID0gJ3NjYXR0ZXIgcGxvdDogeSB2ZXJzdXMgeDEnLAogIHhsYWIgPSAneDEnLAogIHlsYWIgPSAneScKKQpwbG90KAogIHhfZ3JpZCR4MiwgeSwKICBtYWluID0gJ3NjYXR0ZXIgcGxvdDogeSB2ZXJzdXMgeDInLAogIHhsYWIgPSAneDInLAogIHlsYWIgPSAneScKKQoKcGxvdCgKICB4X2dyaWQkeDEsIGxvZyh5KSwKICBtYWluID0gJ3NjYXR0ZXIgcGxvdDogbG9nKHkpIHZlcnN1cyB4MScsCiAgeGxhYiA9ICd4MScsCiAgeWxhYiA9ICdsb2coeSknCikKcGxvdCgKICB4X2dyaWQkeDIsIGxvZyh5KSwKICBtYWluID0gJ3NjYXR0ZXIgcGxvdDogbG9nKHkpIHZlcnN1cyB4MicsCiAgeGxhYiA9ICd4MicsCiAgeWxhYiA9ICdsb2coeSknCikKYGBgCgojIyBFeGFtcGxlIDMuMiBTdXBlcm1hcmtldCBTY2FubmVyIERhdGEKCkRhdGEgbm90IGF2YWlsYWJsZSBvbmxpbmUuCgojIyBFeGFtcGxlIDMuMyBNdW5pY2ggUmVudCBJbmRleCDigJQgVmFyaWFibGUgVHJhbnNmb3JtYXRpb24KCkltcG9ydGluZyBkYXRhIHVzaW5nIHNjcmlwdDoKCmBgYHtyfQpzb3VyY2UoImltcG9ydF9kYXRhL211bmljaF9yZW50X2luZGV4LlIiKQpgYGAKCldlIGRvIHRoZSByZWdyZXNzaW9uIG1vZGVsOgoKJCQKXHRleHR7cmVudHNxbX1fe2l9ID0gXGJldGFfezB9ICsgXGJldGFfezF9IFxjZG90IGYoXHRleHR7YXJlYX1fe2l9KSArIFxlcHNpbG9uX3tpfQokJAoKV2l0aCBib3RoICRmKFxjZG90KSQ6CgokJApmKFx0ZXh0e2FyZWF9X3tpfSkgPSBcdGV4dHthcmVhfV97aX0KJCQKCkZvciBsaW5lYXIgbW9kZWwgYW5kOgoKJCQKZihcdGV4dHthcmVhfV97aX0pID0gXGZyYWN7MX17XHRleHR7YXJlYX1fe2l9fQokJAoKRm9yIGZ1dHVyZSB1c2Ugd2UgZGVmaW5lIHRob3NlIG1vZGVsczoKCioqTW9kZWwgMSAoJE0xJCkqKjoKCiQkClxtYXRoYmJ7RX0oXHRleHR7cmVudHFzbX0pID0gXGJldGFfezB9ICsgXGJldGFfezF9IFx0ZXh0e2FyZWF9CiQkCgoqKk1vZGVsIDIgKCRNMiQpKio6CgokJApcbWF0aGJie0V9KFx0ZXh0e3JlbnRxc219KSA9IFxiZXRhX3swfSArIFxiZXRhX3sxfSBcZnJhY3sxfXtcdGV4dHthcmVhfX0KJCQKCkZvciBub25saW5lYXIgcmVsYXRpb25zaGlwIGJldHdlZW4gJFx0ZXh0e3JlbnRzcW19JCBhbmQgJFx0ZXh0e2FyZWF9JC4gVGhlIHBsb3R0ZWQgZ3JhcGggYmVsb3cgYXJlIHRob3NlIHR3byBtb2RlbHMgd2l0aCB0aGUgbGVmdCBwYW5lbHMgdGhlIGxpbmVhciByZWdyZXNzaW9uIHdpdGhvdXQgdGhlIHRyYW5zZm9ybWF0aW9uIGFuZCB0aGUgcmlnaHQgcGFuZWxzIHdpdGggdGhlIGludmVyc2UgdHJhbnNmb3JtYXRpb24uCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEyLCBmaWcuYWxpZ249J2NlbnRlcid9CnBhcihtZnJvdyA9IGMoMywgMikpCgptMSA8LSBsbShyZW50c3FtIH4gYXJlYSwgZGF0YSA9IG11bmljaF9yZW50X2luZGV4KQpzdW1tYXJ5KG0xKQoKbTIgPC0gbG0ocmVudHNxbSB+IEkoMSAvIGFyZWEpLCBkYXRhID0gbXVuaWNoX3JlbnRfaW5kZXgpCnN1bW1hcnkobTIpCgpwbG90X3JlbnRzcW1fYXJlYSA8LSBmdW5jdGlvbih0aXRsZSkgewogIHBsb3QoCiAgICBtdW5pY2hfcmVudF9pbmRleCRhcmVhLAogICAgbXVuaWNoX3JlbnRfaW5kZXgkcmVudHNxbSwKICAgIG1haW4gPSB0aXRsZSwKICAgIHhsYWIgPSAnYXJlYSBpbiBzcW0nLAogICAgeWxhYiA9ICdyZW50IHBlciBzcW0nCiAgKQp9CgpzZXFfYXJlYSA8LSBzZXEobWluKG11bmljaF9yZW50X2luZGV4JGFyZWEpLCBtYXgobXVuaWNoX3JlbnRfaW5kZXgkYXJlYSksIGxlbmd0aC5vdXQgPSAxMDApCgpwbG90X3JlbnRzcW1fYXJlYSgncmVudCBwZXIgc3FtIHZzLiBhcmVhJykKbTFfYmV0YSA8LSBtMSRjb2VmZmljaWVudHMKbGluZXMoc2VxX2FyZWEsIG0xX2JldGFbMV0gKyBtMV9iZXRhWzJdICogc2VxX2FyZWEsIGNvbCA9ICdyZWQnLCBsd2QgPSAyKQoKcGxvdF9yZW50c3FtX2FyZWEoJ2YoYXJlYSkgPSAxL2FyZWEnKQptMl9iZXRhIDwtIG0yJGNvZWZmaWNpZW50cwpsaW5lcyhzZXFfYXJlYSwgbTJfYmV0YVsxXSArIG0yX2JldGFbMl0gKiAxIC8gc2VxX2FyZWEsIGNvbCA9ICdyZWQnLCBsd2QgPSAyKQoKcGxvdF9yZXNpZHVhbHMgPC0gZnVuY3Rpb24obW9kZWwpIHsKICBwbG90KAogICAgbXVuaWNoX3JlbnRfaW5kZXgkYXJlYSwKICAgIG1vZGVsJHJlc2lkdWFscywKICAgIG1haW4gPSAncmVzaWR1YWxzJywKICAgIHhsYWIgPSAnYXJlYSBpbiBzcW0nLAogICAgeWxhYiA9ICdyZXNpZHVhbHMnCiAgKQogIGFibGluZSgwLCAwLCBjb2wgPSAncmVkJywgbHdkID0gMikKfQoKcGxvdF9yZXNpZHVhbHMobTEpCnBsb3RfcmVzaWR1YWxzKG0yKQoKcGxvdF9hdl9yZXNpZHVhbHMgPC0gZnVuY3Rpb24obW9kZWwpIHsKICBhdl9yZXNpZHVhbHMgPC0gdGFwcGx5KAogICAgbW9kZWwkcmVzaWR1YWxzLCBtdW5pY2hfcmVudF9pbmRleCRhcmVhLAogICAgbWVhbgogICkKICBwbG90KAogICAgdW5pcXVlKG11bmljaF9yZW50X2luZGV4JGFyZWEpLAogICAgYXZfcmVzaWR1YWxzLAogICAgbWFpbiA9ICdhdmVyYWdlIHJlc2lkdWFscycsCiAgICB4bGFiID0gJ2FyZWEgaW4gc3FtJywKICAgIHlsYWIgPSAncmVzaWR1YWxzJwogICkKICBhYmxpbmUoMCwgMCwgY29sID0gJ3JlZCcsIGx3ZCA9IDIpCn0KCnBsb3RfYXZfcmVzaWR1YWxzKG0xKQpwbG90X2F2X3Jlc2lkdWFscyhtMikKYGBgCgoKIyMgRXhhbXBsZSAzLjQgTXVuaWNoIFJlbnQgSW5kZXgg4oCUIFBvbHlub21pYWwgUmVncmVzc2lvbgoKRm9yIHBvbHlub21pYWwgcmVncmVzc2lvbnMgd2UgYWRqdXN0IHR3byBkaWZmZXJlbnQgbW9kZWwuIE9mIHNlY29uZC1vcmRlciAoKipNb2RlbCAzICgkTTMkKSoqKToKCiQkClxtYXRoYmJ7RX0oXHRleHR7cmVudHFzbX0pID0gXGJldGFfezB9ICsgXGJldGFfezF9IFx0ZXh0e2FyZWF9ICsgXGJldGFfezJ9IFx0ZXh0e2FyZWF9XnsyfQokJAoKQW5kIHRoaXJkLW9yZGVyICgqKk1vZGVsIDQgKCRNNCQpKiopOgoKJCQKXG1hdGhiYntFfShcdGV4dHtyZW50cXNtfSkgPSBcYmV0YV97MH0gKyBcYmV0YV97MX0gXHRleHR7YXJlYX0gKyBcYmV0YV97Mn0gXHRleHR7YXJlYX1eezJ9ICsgXGJldGFfezN9IFx0ZXh0e2FyZWF9XnszfQokJAoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMiwgZmlnLmFsaWduPSdjZW50ZXInfQpwYXIobWZyb3cgPSBjKDIsIDIpKQoKbTMgPC0gbG0ocmVudHNxbSB+IGFyZWEgKyBJKGFyZWFeMiksIGRhdGEgPSBtdW5pY2hfcmVudF9pbmRleCkKc3VtbWFyeShtMykKCm00IDwtIGxtKHJlbnRzcW0gfiBhcmVhICsgSShhcmVhXjIpICsgSShhcmVhXjMpLCBkYXRhID0gbXVuaWNoX3JlbnRfaW5kZXgpCnN1bW1hcnkobTQpCgpwbG90X3JlbnRzcW1fYXJlYSgncmVudCBwZXIgc3FtIHZzLiBhcmVhJykKbTNfYmV0YSA8LSBtMyRjb2VmZmljaWVudHMKbGluZXMoc2VxX2FyZWEsIG0zX2JldGFbMV0gKyBtM19iZXRhWzJdICogc2VxX2FyZWEgKyBtM19iZXRhWzNdICogc2VxX2FyZWEgXiAyLCBjb2wgPSAncmVkJywgbHdkID0gMikKCnBsb3RfcmVudHNxbV9hcmVhKCdmKGFyZWEpID0gMS9hcmVhJykKbTRfYmV0YSA8LSBtNCRjb2VmZmljaWVudHMKbGluZXMoc2VxX2FyZWEsIG00X2JldGFbMV0gKyBtNF9iZXRhWzJdICogc2VxX2FyZWEgKyBtNF9iZXRhWzNdICogc2VxX2FyZWEgXiAyICsgbTRfYmV0YVs0XSAqIHNlcV9hcmVhIF4gMywgY29sID0gJ3JlZCcsIGx3ZCA9IDIpCgpwbG90X3Jlc2lkdWFscyhtMykKcGxvdF9yZXNpZHVhbHMobTQpCmBgYAoKIyMgRXhhbXBsZSAzLjUgTXVuaWNoIFJlbnQgSW5kZXgg4oCUIEFkZGl0aXZlIE1vZGVscwoKV2UgZGVmaW5lIHRoZSBmb2xsb3dpbmcgYWRpdGl2ZSBtb2RlbHMuIEFkZGl0aXZlIG1vZGVsIDE6CgokJApcbWF0aGNhbHtFfShcdGV4dHtyZW50c3FtfV97aX0pID0gXGJldGFfezB9ICsgXGJldGFfezF9IFxjZG90IFxmcmFjezF9e1x0ZXh0e2FyZWF9X3tpfX0gKyBcYmV0YV97Mn0gXGNkb3QgXHRleHR7eWVhcmN9X3tpfQokJAoKQW5kIGEgc2Vjb25kIG9uZSB3aXRoICRcdGV4dHt5ZWFyY30kIGFzIGEgcG9saW5vbWlhbCBvZiBkZWdyZWUgMzoKCiQkClxtYXRoY2Fse0V9KFx0ZXh0e3JlbnRzcW19X3tpfSkgPSBcYmV0YV97MH0gKyBcYmV0YV97MX0gXGNkb3QgXGZyYWN7MX17XHRleHR7YXJlYX1fe2l9fSArIFxiZXRhX3syfSBcY2RvdCBcdGV4dHt5ZWFyY31fe2l9ICsgXGJldGFfezN9IFxjZG90IHtcdGV4dHt5ZWFyY31fe2l9fV57Mn0gKyBcYmV0YV97NH0gXGNkb3Qge1x0ZXh0e3llYXJjfV97aX19XnszfQokJAoKYGBge3J9CmFkZGl0aXZlX20xIDwtIGxtKHJlbnRzcW0gfiBJKDEvYXJlYSkgKyB5ZWFyYywgZGF0YSA9IG11bmljaF9yZW50X2luZGV4KQpzdW1tYXJ5KGFkZGl0aXZlX20xKQoKYWRkaXRpdmVfbTIgPC0gbG0ocmVudHNxbSB+IEkoMS9hcmVhKSArIHllYXJjICsgSSh5ZWFyYyBeIDIpKyBJKHllYXJjIF4gMyksIGRhdGEgPSBtdW5pY2hfcmVudF9pbmRleCkKc3VtbWFyeShhZGRpdGl2ZV9tMikKYGBgCgpBIHRoaXJkIG1vZGVsIGlzIHNwZWNpZmllZCB1c2luZyB0aGUgdHJ1bmNhdGVkIHllYXIgb2YgY29uc3RydWN0aW9uICgkXHRleHR7eWVhcmN9IC0gMTkwMCQpOgoKYGBge3J9Cm11bmljaF9yZW50X2luZGV4JHllYXJjbyA8LSBtdW5pY2hfcmVudF9pbmRleCR5ZWFyYyAtIDE5MDAKYWRkaXRpdmVfbTMgPC0gbG0ocmVudHNxbSB+IEkoMS9hcmVhKSArIHllYXJjbyArIEkoeWVhcmNvIF4gMikrIEkoeWVhcmNvIF4gMyksIGRhdGEgPSBtdW5pY2hfcmVudF9pbmRleCkKc3VtbWFyeShhZGRpdGl2ZV9tMykKYGBgCgpBcyBpbiB0aGUgYm9vaywgd2Ugc2hvdyB0aGUgZWZmZWN0IG9mIHllYXIgb2YgY29uc3RydWN0aW9uIGluIHRoZSBwb2x5bm9taWFsIG9mIGRlZ3JlZSAzOgoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD00LCBmaWcuYWxpZ249J2NlbnRlcid9CnBhcihtZnJvdyA9IGMoMSwgMikpCmJldGFfYWRkX20yIDwtIGFkZGl0aXZlX20yJGNvZWZmaWNpZW50cwoKeWVhcmNfc2VxIDwtIHNlcShtaW4obXVuaWNoX3JlbnRfaW5kZXgkeWVhcmMpLCBtYXgobXVuaWNoX3JlbnRfaW5kZXgkeWVhcmMpLCBsZW5ndGgub3V0ID0gMTAwKQoKcGxvdF9lZmZlY3QgPC0gZnVuY3Rpb24oYmV0YSwgeWVhcl9zZXEsIHRpdGxlKXsKICBwbG90KAogICAgeWVhcl9zZXEsCiAgICBiZXRhWzNdICogeWVhcl9zZXEgKyBiZXRhWzRdICogeWVhcl9zZXFeMiArIGJldGFbNV0gKiB5ZWFyX3NlcV4zLAogICAgbWFpbiA9IHRpdGxlLAogICAgeGxhYiA9ICd5ZWFyIG9mIGNvbnN0cnVjdGlvbicsCiAgICB5bGFiID0gJ2VmZmVjdCBvZiB5ZWFyIG9mIGNvbnN0cnVjdGlvbicsCiAgICB0eXBlID0gJ2wnLAogICAgY29sID0gJ2RhcmtibHVlJywKICAgIGx3ZCA9IDIKICApCn0KCnBsb3RfZWZmZWN0KGJldGFfYWRkX20yLCB5ZWFyY19zZXEsICdlZmZlY3Qgb2YgeWVhciBvZiBjb25zdHJ1Y3Rpb24nKQoKYmV0YV9hZGRfbTMgPC0gYWRkaXRpdmVfbTMkY29lZmZpY2llbnRzCnRydW5jYXRlX3llYXJjX3NlcSA8LSBzZXEobWluKG11bmljaF9yZW50X2luZGV4JHllYXJjbyksIG1heChtdW5pY2hfcmVudF9pbmRleCR5ZWFyY28pLCBsZW5ndGgub3V0ID0gMTAwKQpwbG90X2VmZmVjdChiZXRhX2FkZF9tMywgdHJ1bmNhdGVfeWVhcmNfc2VxLCAnZWZmZWN0IG9mIHllYXIgb2YgY29uc3RydWN0aW9uLCBjb2RlZCBmcm9tIDE4IHRvIDk2JykKYGBgCgpBIG5ldyBtb2RlbCBpcyBkZWZpbmVkIHVzaW5nIHRoZSBvcnRob2dvbmFsIHBvbHlub21pYWwgb2YgeWVhciBvZiBjb25zdHJ1Y3Rpb246CgpgYGB7cn0KbXVuaWNoX3JlbnRfaW5kZXgkYXJlYWludiA8LSAoMSAvIG11bmljaF9yZW50X2luZGV4JGFyZWEpIC0gbWVhbigxIC8gbXVuaWNoX3JlbnRfaW5kZXgkYXJlYSkKcG9seV95ZWFyIDwtIHBvbHkobXVuaWNoX3JlbnRfaW5kZXgkeWVhcmNvLCAzKQptdW5pY2hfcmVudF9pbmRleCR5ZWFyY2MgPC0gcG9seV95ZWFyWywgMV0KbXVuaWNoX3JlbnRfaW5kZXgkeWVhcmNjMiA8LSBwb2x5X3llYXJbLCAyXQptdW5pY2hfcmVudF9pbmRleCR5ZWFyY2MzIDwtIHBvbHlfeWVhclssIDNdCmFkZGl0aXZlX200IDwtIGxtKHJlbnRzcW0gfiBhcmVhaW52ICsgeWVhcmNjICsgeWVhcmNjMiArIHllYXJjYzMsIGRhdGEgPSBtdW5pY2hfcmVudF9pbmRleCkKc3VtbWFyeShhZGRpdGl2ZV9tNCkKYGBgCgpOb3cgd2UgY2FsY3VsYXRlIHRoZSBwYXJ0aWFsIHJlc2lkdWFscyBmb3IgJFx0ZXh0e2FyZWF9JCBhbmQgJFx0ZXh0e3llYXJjfSQgZGVmaW5lIGJ5OgoKJCQKXGhhdHtcZXBzaWxvbn1fe1x0ZXh0e2FyZWF9LCBpfSA9IFx0ZXh0e3JlbnRzcW19X3tpfSAtIFxoYXR7XGJldGFfezB9fSAtIFxoYXR7Zn1fezJ9KFx0ZXh0e3llYXJjfV97aX0pCiQkCgpXaXRoIHRoZSBlZmZlY3QgJGYoXHRleHR7eWVhcmN9X3tpfSkkOgoKJCQKXGhhdHtmfV97Mn0oXHRleHR7eWVhcmN9KSA9IFxiZXRhX3syfSBcY2RvdCBcdGV4dHt5ZWFyY31fe2l9ICsgXGJldGFfezN9IFxjZG90IHtcdGV4dHt5ZWFyY31fe2l9fV57Mn0gKyBcYmV0YV97NH0gXGNkb3Qge1x0ZXh0e3llYXJjfV97aX19XnszfQokJAoKQW5kOgoKJCQKXGhhdHtcZXBzaWxvbn1fe1x0ZXh0e3llYXJjfSwgaX0gPSBcdGV4dHtyZW50c3FtfV97aX0gLSBcaGF0e1xiZXRhX3swfX0gLSBcaGF0e2Z9X3sxfShcdGV4dHthcmVhfV97aX0pCiQkCgpXaXRoIHRoZSBlZmZlY3QgJGYoXHRleHR7eWVhcmN9X3tpfSkkOgoKJCQKXGhhdHtmfV97MX0oXHRleHR7eWVhcmN9KSA9IFxiZXRhX3sxfSBcY2RvdCBcZnJhY3sxfXtcdGV4dHthcmVhfV97aX19CiQkCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTQsIGZpZy5hbGlnbj0nY2VudGVyJ30KcGFyKG1mcm93ID0gYygxLCAyKSkKCmJldGFfYWRkX200IDwtIGFkZGl0aXZlX200JGNvZWZmaWNpZW50cwoKYXJlYV9zZXEgPC0gc2VxKG1pbihtdW5pY2hfcmVudF9pbmRleCRhcmVhKSwgbWF4KG11bmljaF9yZW50X2luZGV4JGFyZWEpLCBsZW5ndGgub3V0ID0gMTAwKQoKYXJlYV9lZmZlY3QgPC0gZnVuY3Rpb24oYXJlYSl7CiAgaW52X2FyZWEgPC0gMSAvIGFyZWEKICBjZW50X2FyZWEgPC0gaW52X2FyZWEgLSBtZWFuKGludl9hcmVhKQogIGJldGFfYWRkX200WzJdICogY2VudF9hcmVhCn0KCnllYXJjX2VmZmVjdCA8LSBmdW5jdGlvbih5ZWFyYyl7CiAgcG9seV95ZWFyYyA8LSBwcmVkaWN0KHBvbHlfeWVhciwgeWVhcmMgLSAxOTAwKQogIGJldGFfYWRkX200WzNdICogcG9seV95ZWFyY1ssIDFdICsgYmV0YV9hZGRfbTRbNF0gKiBwb2x5X3llYXJjWywgMl0gKyBiZXRhX2FkZF9tNFs1XSAqIHBvbHlfeWVhcmNbLCAzXQp9CgpyZXNpZHVhbF9hcmVhIDwtIG11bmljaF9yZW50X2luZGV4JHJlbnRzcW0gLSBiZXRhX2FkZF9tNFsxXSAtIHllYXJjX2VmZmVjdChtdW5pY2hfcmVudF9pbmRleCR5ZWFyYykKcGxvdCgKICBtdW5pY2hfcmVudF9pbmRleCRhcmVhLAogIHJlc2lkdWFsX2FyZWEsCiAgbWFpbiA9ICdlZmZlY3Qgb2YgYXJlYScsCiAgeGxhYiA9ICdhcmVhIGluIHNxbScsCiAgeWxhYiA9ICdlZmZlY3Qgb2YgYXJlYSAvIHBhcnRpYWwgcmVzaWR1YWxzJwopCmxpbmVzKGFyZWFfc2VxLCBhcmVhX2VmZmVjdChhcmVhX3NlcSksIGNvbCA9ICdyZWQnLCBsd2QgPSAyKQoKcmVzaWR1YWxfeWVhciA8LSBtdW5pY2hfcmVudF9pbmRleCRyZW50c3FtIC0gYmV0YV9hZGRfbTRbMV0gLSBhcmVhX2VmZmVjdChtdW5pY2hfcmVudF9pbmRleCRhcmVhKQpwbG90KAogIG11bmljaF9yZW50X2luZGV4JHllYXJjLAogIHJlc2lkdWFsX3llYXIsCiAgbWFpbiA9ICdlZmZlY3Qgb2YgeWVhciBvZiBjb25zdHJ1Y3Rpb24nLAogIHhsYWIgPSAneWVhciBvZiBjb25zdHJ1Y3Rpb24nLAogIHlsYWIgPSAnZWZmZWN0IG9mIHllYXIgb2YgY29uc3RydWN0aW9uIC8gcGFydGlhbCByZXNpZHVhbHMnCikKbGluZXMoeWVhcmNfc2VxLCB5ZWFyY19lZmZlY3QoeWVhcmNfc2VxKSwgY29sID0gJ3JlZCcsIGx3ZCA9IDIpCmBgYAoKIyMgRXhhbXBsZSAzLjYgTXVuaWNoIFJlbnQgSW5kZXgg4oCUIEVmZmVjdCBDb2RpbmcKCldlIGFkanVzdCB0aGUgcmVncmVzc2lvbiBtb2RlbCB3aXRoIHRoZSBjb2RpbmcgdmFsdWVzIG9mIGxvY2F0aW9uOgoKJCQKXG1hdGhiYntFfShcdGV4dHtyZW50c3FtfV97aX0pID0gXGJldGFfezB9ICsgXGJldGFfezF9IFxjZG90IFx0ZXh0e2dsb2NhdGlvbn1fezF9ICsgXGJldGFfezJ9IFxjZG90IFx0ZXh0e3Rsb2NhdGlvbn1fezF9CiQkCgpgYGB7cn0KbXVuaWNoX3JlbnRfaW5kZXgkZ2xvY2F0aW9uIDwtIDAKbXVuaWNoX3JlbnRfaW5kZXgkZ2xvY2F0aW9uW211bmljaF9yZW50X2luZGV4JGxvY2F0aW9uID09IDJdIDwtIDEKbXVuaWNoX3JlbnRfaW5kZXgkZ2xvY2F0aW9uW211bmljaF9yZW50X2luZGV4JGxvY2F0aW9uID09IDFdIDwtIC0xCgptdW5pY2hfcmVudF9pbmRleCR0bG9jYXRpb24gPC0gMAptdW5pY2hfcmVudF9pbmRleCR0bG9jYXRpb25bbXVuaWNoX3JlbnRfaW5kZXgkbG9jYXRpb24gPT0gM10gPC0gMQptdW5pY2hfcmVudF9pbmRleCR0bG9jYXRpb25bbXVuaWNoX3JlbnRfaW5kZXgkbG9jYXRpb24gPT0gMV0gPC0gLTEKCmNvZF9tb2RlbCA8LSBsbSgKICByZW50c3FtIH4gZ2xvY2F0aW9uICsgdGxvY2F0aW9uLAogIGRhdGEgPSBtdW5pY2hfcmVudF9pbmRleAopCnN1bW1hcnkoY29kX21vZGVsKQpgYGAKCiMjIEV4YW1wbGUgMy43IE11bmljaCBSZW50IEluZGV4IOKAlCBJbnRlcmFjdGlvbiB3aXRoIFF1YWxpdHkgb2YgS2l0Y2hlbgoKV2UgaW1wb3J0IHRoZSBuZXcgdXBkYXRlZCBtb2RlbCBvZiAyMDAxLCBhdmFpbGFibGUgYXQgW29mZmljaWFsIHBhZ2Ugb2YgdGhlIGRhdGFzZXRdKGh0dHBzOi8vd3d3LnVuaS1nb2V0dGluZ2VuLmRlL2VuLzU1MTYyNS5odG1sKSBhcyBhbGwgdGhlIGRhdGFzZXRzOgoKYGBge3J9Cm11bmljaF9yZW50X3VybF8wMSA8LSAiaHR0cHM6Ly93d3cudW5pLWdvZXR0aW5nZW4uZGUvZGUvZG9jdW1lbnQvZG93bmxvYWQvZTAwZDAzOWU5YzFmMjhhYjQwNGIyN2IwYjE0OTMxZjEucmF3L3JlbnQ5OF8wMC5yYXciCgptdW5pY2hfcmVudF9pbmRleF8wMSA8LSByZWFkLnRhYmxlKAogIHVybChtdW5pY2hfcmVudF91cmxfMDEpLAogIGhlYWRlciA9IDEsCiAgY29sQ2xhc3NlcyA9IGMoCiAgICAibnVtZXJpYyIsICJudW1lcmljIiwgIm51bWVyaWMiLAogICAgIm51bWVyaWMiLCAiaW50ZWdlciIsICJpbnRlZ2VyIiwKICAgICJpbnRlZ2VyIiwgImludGVnZXIiLCAiaW50ZWdlciIsCiAgICAiaW50ZWdlciIsICJudW1lcmljIiwgIm51bWVyaWMiLAogICAgIm51bWVyaWMiCiAgKQopCgpzdW1tYXJ5KG11bmljaF9yZW50X2luZGV4XzAxKQpgYGAKCkFuZCB3ZSBhZGp1c3QgdGhlIG1vZGVsOgoKJCQKXGJlZ2lue21hdHJpeH0KICBcd2lkZWhhdHtcdGV4dHtyZW50c3FtfX1fe2l9ID0gJiBcYmV0YV97MH0gKyBcYmV0YV97MX0gXHRleHR7YXJlYWludmN9X3tpfSArIFxiZXRhX3syfSBcdGV4dHt5ZWFyY299X3tpfSArIFxiZXRhX3szfSBcdGV4dHt5ZWFyY28yfV97aX0gKyBcYmV0YV97NH0gXHRleHR7eWVhcmNvM31fe2l9IFxcCiAgICYgKyBcYmV0YV97NX0gXHRleHR7eWVhcjAxfV97aX0gKyBcYmV0YV97Nn0gXHRleHR7bmtpdGNoZW59X3tpfSArIFxiZXRhX3s3fSBcdGV4dHtwa2l0Y2hlbn1fe2l9IFxcCiAgICYgKyBcYmV0YV97OH0gXHRleHR7bmtpdGNoZW59X3tpfSBcY2RvdCBcdGV4dHt5ZWFyMDF9X3tpfSArIFxiZXRhX3s5fSBcdGV4dHtwa2l0Y2hlbn1fe2l9IFxjZG90IFx0ZXh0e3llYXIwMX1fe2l9ClxlbmR7bWF0cml4fQokJAoKYGBge3J9Cm11bmljaF9yZW50X2luZGV4XzAxJGFyZWFpbnZjIDwtIG11bmljaF9yZW50X2luZGV4XzAxJGludmFyZWEgLSBtZWFuKG11bmljaF9yZW50X2luZGV4XzAxJGludmFyZWEpCm11bmljaF9yZW50X2luZGV4XzAxJHllYXJjbyA8LSBtdW5pY2hfcmVudF9pbmRleF8wMSR5ZWFyYyAtIG1lYW4obXVuaWNoX3JlbnRfaW5kZXhfMDEkeWVhcmMpCnBvbHlfeWVhcl8wMSA8LSBwb2x5KG11bmljaF9yZW50X2luZGV4XzAxJHllYXJjbywgMykKbXVuaWNoX3JlbnRfaW5kZXhfMDEkeWVhcmNvIDwtIHBvbHlfeWVhcl8wMVssIDFdCm11bmljaF9yZW50X2luZGV4XzAxJHllYXJjbzIgPC0gcG9seV95ZWFyXzAxWywgMl0KbXVuaWNoX3JlbnRfaW5kZXhfMDEkeWVhcmNvMyA8LSBwb2x5X3llYXJfMDFbLCAzXQoKaW50ZXJhY3Rpb25fbW9kZWwgPC0gbG0oCiAgcmVudHNxbV9ldXJvIH4gYXJlYWludmMgKyB5ZWFyY28gKyB5ZWFyY28yICsgeWVhcmNvMyArIHllYXIwMSArIG5raXRjaGVuICsgcGtpdGNoZW4gKyBua2l0Y2hlbiAqIHllYXIwMSArIHBraXRjaGVuICogeWVhcjAxLAogIGRhdGEgPSBtdW5pY2hfcmVudF9pbmRleF8wMQopCgpzdW1tYXJ5KGludGVyYWN0aW9uX21vZGVsKQpgYGAKCiMjIEV4YW1wbGUgMy44IE11bmljaCBSZW50IEluZGV4IOKAlCBJbnRlcmFjdGlvbiBCZXR3ZWVuIExpdmluZyBBcmVhIGFuZCBMb2NhdGlvbgoKVGhlIG1vZGVsIHRvIGFkanVzdCBpdXMgZGVmaW5lZCBhczoKCiQkClxtYXRoYmJ7RX0oXHRleHR7cmVudHNxbX1fe2l9KSA9IFxiZXRhX3swfSArIFxiZXRhX3sxfSBcY2RvdCBcdGV4dHt0bG9jYXRpb259ICsgXGJldGFfezJ9IFxjZG90IFxvdmVybGluZXtcZnJhY3sxfXtcdGV4dHthcmVhfX19ICsgXGJldGF7M30gXGNkb3QgXHRleHR7YXJlYX0gXGNkb3QgXHRleHR7dGxvY2F0aW9ufQokJAoKV2UgdXNlIHRoZSBkYXRhc2V0IGZvciBhdmVyYWdlIG9yIHRvcCBsb2NhdGlvbiBvbmx5IGFuZCBjaGFuZ2UgdGhlIGR1bW15IHZhcmlhYmxlICRcdGV4dHt0bG9jYXRpb259JCB0byAkMCQgKGF2ZXJhZ2UpIGFuZCAkMSQgKHRvcCkuIEZpbmFsbHksIHdlIHZpc3VhbGl6ZSB0aGUgZWZmZWN0IGFzIGRlc2NyaWJlZCBpbiB0aGUgYm9vay4KCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSdjZW50ZXInfQpwYXIobWZyb3cgPSBjKDEsIDIpKQphdF9yZW50IDwtIHN1YnNldChtdW5pY2hfcmVudF9pbmRleCwgbG9jYXRpb24gPT0gMSB8IGxvY2F0aW9uID09IDMpCmF0X3JlbnQkdGxvY2F0aW9uW2F0X3JlbnQkbG9jYXRpb24gPT0gMV0gPC0gMApzdW1tYXJ5KGF0X3JlbnQpCgphcmVhX2xvY2F0aW9uX21vZGVsIDwtIGxtKAogIHJlbnRzcW0gfiB0bG9jYXRpb24gKyBhcmVhaW52ICsgYXJlYTp0bG9jYXRpb24sCiAgZGF0YSA9IGF0X3JlbnQKKQpzdW1tYXJ5KGFyZWFfbG9jYXRpb25fbW9kZWwpCgpiZXRhX2FsIDwtIGFyZWFfbG9jYXRpb25fbW9kZWwkY29lZmZpY2llbnRzCgpmMV9hcmVhIDwtIGZ1bmN0aW9uKGFyZWEpewogIGFyZWFpbnZjIDwtICgxIC8gYXJlYSkgLSBtZWFuKDEgLyBhcmVhKQogIGJldGFfYWxbM10gKiBhcmVhaW52Ywp9CgpmMl9hcmVhIDwtIGZ1bmN0aW9uKGFyZWEpewogIGJldGFfYWxbNF0gKiBhcmVhCn0KCnNtYWxsX2VmZmVjdCA8LSBmMV9hcmVhKGFyZWFfc2VxKQpiaWdfZWZmZWN0IDwtIGJldGFfYWxbMl0gKyBmMV9hcmVhKGFyZWFfc2VxKSArIGYyX2FyZWEoYXJlYV9zZXEpCgp5bGltIDwtIGMobWluKGMoc21hbGxfZWZmZWN0LCBiaWdfZWZmZWN0KSksIG1heChjKHNtYWxsX2VmZmVjdCwgYmlnX2VmZmVjdCkpKQoKcGxvdCgKICBhcmVhX3NlcSwKICBzbWFsbF9lZmZlY3QsCiAgbWFpbiA9ICdhcmVhIGVmZmVjdHMgYmFzZWQgb24gYSBsaW5lYXIgaW50ZXJhY3Rpb24nLAogIHhsYWIgPSAnYXJlYSBpbiBzcW0nLAogIHlsYWIgPSAnYXJlYSBlZmZlY3RzJywKICB5bGltID0geWxpbSwKICB0eXBlID0gJ2wnLAogIGNvbCA9ICdkYXJrYmx1ZScsCiAgbHdkID0gMgopCmxpbmVzKGFyZWFfc2VxLCBiaWdfZWZmZWN0LCBjb2wgPSAncmVkJywgbHdkID0gMikKCnBsb3QoCiAgYXJlYV9zZXEsCiAgYmV0YV9hbFsyXSArIGYyX2FyZWEoYXJlYV9zZXEpLAogIG1haW4gPSAndmFyeWluZyBlZmZlY3Qgb2YgdG9wIGxvY2F0aW9uJywKICB4bGFiID0gJ2FyZWEgaW4gc3FtJywKICB5bGFiID0gJ1ZhcnlpbmcgZWZmZWN0IG9mIHRvcCBsb2NhdGlvbicsCiAgdHlwZSA9ICdsJywKICBjb2wgPSAnZGFya2JsdWUnLAogIGx3ZCA9IDIKKQpgYGAKCiMjIEV4YW1wbGUgMy45IE9ydGhvZ29uYWwgRGVzaWduCgpKdXN0IGZvciBleGVtcGxpZnksIHdlIGRlZmluZSB0aGUgb3J0aG9nb25hbCBwcm9jZXNzIGRlc2NyaWJlIGFzIGFuIFIgZnVuY3Rpb246CgokJApcdGlsZGV7eH1ee2p9ID0geF57an0gLSBcd2lkZXRpbGRle1h9X3tqfSBcbGVmdCAoIHtcd2lkZXRpbGRle1h9X3tqfX1ee1R9IFx3aWRldGlsZGV7WH1fe2p9IFxyaWdodCApXnstMX0ge1x3aWRldGlsZGV7WH1fe2p9fV57VH0geF57an0KJCQKCmBgYHtyfQpvcnRob2dvbmFsX2Rlc2lnbiA8LSBmdW5jdGlvbih4KXsKICB4X291dCA8LSBjYmluZCh4WywgMV0gLSBtZWFuKHhbLCAxXSksIG1hdHJpeCgwLCBucm93ID0gbnJvdyh4KSwgbmNvbCA9IG5jb2woeCkgLSAxKSkKICBmb3IgKGkgaW4gMjpuY29sKHgpKXsKICAgIHhfaGF0IDwtIGFzLm1hdHJpeCh4X291dFssIDE6KGktMSldKQogICAgeF9pbnYgPC0gc29sdmUodCh4X2hhdCkgJSolIHhfaGF0KQogICAgeF9vdXRbLCBpXSA8LSB4WywgaV0gLSB4X2hhdCAlKiUgeF9pbnYgJSolIHQoeF9oYXQpICUqJSB4WywgaV0KICB9CiAgeF9vdXQKfQpgYGAKV2UgdGVzdCB0aGlzIGZ1bmN0aW9uIG9uIHRoZSBwb2x5bm9taWFsIG9mIHRoZSAkXHRleHR7eWVhcmN9JCB2YXJpYWJsZToKCmBgYHtyfQpwb2x5X3Rlc3QgPC0gcG9seShtdW5pY2hfcmVudF9pbmRleCR5ZWFyYywgMywgcmF3ID0gVFJVRSkKb3J0aG9nb25hbF90ZXN0IDwtIG9ydGhvZ29uYWxfZGVzaWduKHBvbHlfdGVzdCkKCiMgQW5kIGNoZWNrIGl0CmMoCiAgb3J0aG9nb25hbF90ZXN0WywgMV0gJSolIG9ydGhvZ29uYWxfdGVzdFssIDJdLAogIG9ydGhvZ29uYWxfdGVzdFssIDJdICUqJSBvcnRob2dvbmFsX3Rlc3RbLCAzXSwKICBvcnRob2dvbmFsX3Rlc3RbLCAxXSAlKiUgb3J0aG9nb25hbF90ZXN0WywgM10KKQpgYGAKCldoeSBkaWQgaXQgbm90IHJlc3VsdD8KCiMjIEV4YW1wbGUgMy4xMCBNdW5pY2ggUmVudCBJbmRleCDigJQgQ29tcGFyaXNvbiBvZiBNb2RlbHMgVXNpbmcgJHtSfV57Mn0kCgpXZSBjb21wYXJlIHRoZSBtb2RlbHM6ICRNMSQsICRNMiQsICRNMyQsIGFuZCAkTTQkIHVzaW5nIGl0cyBjb2VmZmljaWVudCBvZiBkZXRlcm1pbmF0aW9uICRSXnsyfSQKCmBgYHtyfQpjb2VmZl9kZXRlciA8LSBkYXRhLmZyYW1lKAogIGBSIFNxdWFyZWRgID0gYyhzdW1tYXJ5KG0xKSRyLnNxdWFyZWQsIHN1bW1hcnkobTIpJHIuc3F1YXJlZCwgc3VtbWFyeShtMykkci5zcXVhcmVkLCBzdW1tYXJ5KG00KSRyLnNxdWFyZWQpLAogIHJvdy5uYW1lcyA9IGMoIk0xIiwgIk0yIiwgIk0zIiwgIk00IikKKQpjb2VmZl9kZXRlcgpgYGAKCiMjIEV4YW1wbGUgMy4xMSBTaW1wbGUgTGluZWFyIFJlZ3Jlc3Npb24KCkZvciB0aGUgbW9kZWw6CgokJAp5X3tpfSA9IFxiZXRhIHhfe2l9ICsgXGVwc2lsb25fe2l9CiQkCgpXZSBjYW4gdmVyaWZ5OgoKJCQKXGxlZnQgKCB7WF97bn19XntUfSBYX3tufSBccmlnaHQgKV57LTF9IFx0byAwCiQkCgpBbmQ6CgokJApcbWF4X3tpPTEsLi4uLG59IHt4X3tpfX1ee1R9IFxsZWZ0ICgge1hfe259fV57VH0gWF97bn0gXHJpZ2h0ICleey0xfSB4X3tpfSBcdG8gMCBcdGV4dHsgZm9yIH0gbiBcdG8gXGluZnR5CiQkCgojIyMgJHhfe2l9ID0gaSQ6CgokJApcbGVmdCAoIHtYX3tufX1ee1R9IFhfe259IFxyaWdodCApXnstMX0gPSBcbGVmdCAoICgxLCAyLCAzLCBcY2RvdHMsIG4pIFxsZWZ0ICggXGJlZ2lue21hdHJpeH0KMSBcXAoyIFxcClx2ZG90cyAgXFwKbgpcZW5ke21hdHJpeH0gXHJpZ2h0ICkgXHJpZ2h0ICleey0xfSA9IFxsZWZ0ICggMSArIDQgKyBcY2RvdHMgKyBuXjIgXHJpZ2h0ICleey0xfSBcdG8gMAokJAoKQW5kOgoKJCQKXG1heF97aT0xLC4uLixufSAoIDEsIDIsIFxjZG90cywgbiApIFxsZWZ0ICggMSArIDQgKyBcY2RvdHMgKyBuXjIgXHJpZ2h0ICleey0xfSBcbGVmdCAoIFxiZWdpbnttYXRyaXh9CjEgXFwKMiBcXApcdmRvdHMgIFxcCm4KXGVuZHttYXRyaXh9IFxyaWdodCApIFx0byAwIFx0ZXh0eyBmb3IgfSBuIFx0byBcaW5mdHkKJCQKCiMjIyAkeF97aX0gPSBcZnJhY3sxfXtpfSQ6CgokJApcbGVmdCAoIHtYX3tufX1ee1R9IFhfe259IFxyaWdodCApXnstMX0gPSBcbGVmdCAoIFxsZWZ0ICgxLCAwLjUsIDAuMzMsIFxjZG90cywgXGZyYWN7MX17bn0gXHJpZ2h0ICkgXGxlZnQgKCBcYmVnaW57bWF0cml4fQoxIFxcCjAuNSBcXApcdmRvdHMgIFxcClxmcmFjezF9e259ClxlbmR7bWF0cml4fSBccmlnaHQgKSBccmlnaHQgKV57LTF9ID0gXGxlZnQgKCBcc3VtX3tpPTF9XntufSBcZnJhY3sxfXtpXnsyfX0gXHJpZ2h0ICleey0xfSBcbm90e1x0b30gMAokJAoKQW5kOgoKJCQKXG1heF97aT0xLC4uLixufSBcbGVmdCAoIDEsIDAuNSwgXGNkb3RzLCBcZnJhY3sxfXtufSBccmlnaHQgKSBcbGVmdCAoIFxzdW1fe2kgPSAxfV57bn0gXGZyYWN7MX17aV57Mn19IFxyaWdodCApXnstMX0gXGxlZnQgKCBcYmVnaW57bWF0cml4fQoxIFxcCjAuNSBcXApcdmRvdHMgIFxcClxmcmFjezF9e259ClxlbmR7bWF0cml4fSBccmlnaHQgKSBcbm90e1x0b30gMCBcdGV4dHsgZm9yIH0gbiBcdG8gXGluZnR5CiQkCgojIyMgJHhfe2l9ID0gXGZyYWN7MX17XHNxcnR7aX19JDoKCiQkClxsZWZ0ICgge1hfe259fV57VH0gWF97bn0gXHJpZ2h0ICleey0xfSA9IFxsZWZ0ICggXHN1bV97aSA9IDF9XntufSBcZnJhY3sxfXtpfSBccmlnaHQgKV57LTF9IFx0byAwCiQkCgpBbmQ6CgokJApcbWF4X3tpPTEsLi4uLG59IFxsZWZ0ICggMSwgXGZyYWN7MX17XHNxcnR7Mn19LCBcY2RvdHMsIFxmcmFjezF9e1xzcXJ0e259fSBccmlnaHQgKSBcbGVmdCAoIFxzdW1fe2kgPSAxfV57bn0gXGZyYWN7MX17aX0gXHJpZ2h0ICleey0xfSBcYmVnaW57bWF0cml4fQoxIFxcClxmcmFjezF9e1xzcXJ0ezJ9fSBcXApcdmRvdHMgXFwKXGZyYWN7MX17XHNxcnR7bn19ClxlbmR7bWF0cml4fSBcdG8gMAokJAoKIyMgRXhhbXBsZSAzLjEyIE11bmljaCBSZW50IEluZGV4IOKAlCBIeXBvdGhlc2lzIFRlc3RpbmcKClRoZSByZWdyZXNzaW9uIG1vZGVsIHRvIGFkanVzdCBpczoKCiQkClxiZWdpbnttYXRyaXh9Clx0ZXh0e3JlbnRzcW19X3tpfSA9ICYgXGJldGFfezB9ICsgXGJldGFfezF9IFx0ZXh0e2FyZWFpbnZjfV97aX0gKyBcYmV0YV97Mn0gXHRleHR7eWVhcmNvfV97aX0gKyBcYmV0YV97M30gXHRleHR7eWVhcmNvMn1fe2l9ICsgXGJldGFfezR9IFx0ZXh0e3llYXJjbzN9X3tpfSBcXAomICsgXGJldGFfezV9IFx0ZXh0e25raXRjaGVufV97aX0gKyBcYmV0YV97Nn0gXHRleHR7cGtpdGNoZW59X3tpfSArIFxiZXRhX3s3fSBcdGV4dHt5ZWFyMDF9X3tpfSArIFxlcHNpbG9uX3tpfQpcZW5ke21hdHJpeH0KJCQKCmBgYHtyfQpoeXBvdGhlc2lzX21vZGVsIDwtIGxtKAogIHJlbnRzcW1fZXVybyB+IGFyZWFpbnZjICsgeWVhcmNvICsgeWVhcmNvMiArIHllYXJjbzMgKyBua2l0Y2hlbiArIHBraXRjaGVuICsgeWVhcjAxLAogIGRhdGEgPSBtdW5pY2hfcmVudF9pbmRleF8wMQopCnN1bW1hcnkoaHlwb3RoZXNpc19tb2RlbCkKYGBgCgpTbyB3ZSB3YW50IHRvIHRlc3QgdGhlIGh5cG90aGVzaXM6CgokJApcYmVnaW57bWF0cml4fQpIX3swfSA6IFxiZXRhX3s3fSA9IDAgJiBcdGV4dHthZ2FpbnN0fSAmIEhfezF9IDogXGJldGFfezd9IFxuZXEgMApcZW5ke21hdHJpeH0KJCQKCkFib3V0IHRoZSBzaWduaWZpY2FuY2Ugb2YgdGhlIGZvbGxvdy11cCBtZWFzdXJlLgoKJCQKXGJlZ2lue21hdHJpeH0KSF97MH0gOiBcbGVmdCAoIFxiZWdpbnttYXRyaXh9ClxiZXRhX3s1fSBcXApcYmV0YV97Nn0KXGVuZHttYXRyaXh9IFxyaWdodCApID0gXGxlZnQgKCBcYmVnaW57bWF0cml4fQowIFxcCjAKXGVuZHttYXRyaXh9IFxyaWdodCApICYgXHRleHR7YWdhaW5zdH0gJiBIX3sxfSA6IFxsZWZ0ICggXGJlZ2lue21hdHJpeH0KXGJldGFfezV9IFxcClxiZXRhX3s2fQpcZW5ke21hdHJpeH0gXHJpZ2h0ICkgXG5lcSBcbGVmdCAoIFxiZWdpbnttYXRyaXh9CjAgXFwKMApcZW5ke21hdHJpeH0gXHJpZ2h0ICkKXGVuZHttYXRyaXh9CiQkCgpBYm91dCB0aGUgc2lnbmlmaWNhbmNlIG9mIHRoZSBraXRjaGVuIHZhcmlhYmxlLiBBbmQgYSBmaW5hbCB0ZXN0IGFib3V0IHRoZSBzaWduaWZpY2FuY2Ugb2YgZGlmZmVyZW50aWF0ZSBiZXR3ZWVuIHRoZXNlIHR3byB0eXBlIG9mIGtpdGNoZW5zOgoKJCQKXGJlZ2lue21hdHJpeH0KSF97MH0gOiBcYmV0YV97NX0gLSBcYmV0YV97Nn0gPSAwICYgXHRleHR7YWdhaW5zdH0gJiBIX3sxfSA6IFxiZXRhX3s1fSAtIFxiZXRhX3s2fSBcbmVxIDAKXGVuZHttYXRyaXh9CiQkCgojIyBFeGFtcGxlIDMuMTMgTXVuaWNoIFJlbnQgSW5kZXgg4oCUIFN0YW5kYXJkIE91dHB1dCBhbmQgSHlwb3RoZXNpcyBUZXN0cwoKRm9yIHRoZSBmaXJzdCB0ZXN0ICgkSF97MH0gOiBcYmV0YV97N30gPSAwJCksIHdlIGNhbGN1bGF0ZToKCiQkCnRfezd9ID0gXGZyYWN7XGhhdHtcYmV0YX1fezd9fXt7XHdpZGVoYXR7XHRleHR7VmFyfShcaGF0e1xiZXRhfV97N30pfX1eezEvMn19IFxzaW0gdF97MSAtIFxhbHBoYSAvIDJ9IChuIC0gcCkKJCQKClRoZSBjb3ZhcmlhbmNlIG1hdHJpeCBpczoKCmBgYHtyfQpjb3ZfbWF0cml4IDwtIHZjb3YoaHlwb3RoZXNpc19tb2RlbCkKY292X21hdHJpeApgYGAKCldlIGNoZWNrIHRoZSAkXHRleHR7VmFyfShcaGF0e1xiZXRhfV97N30pJCBpcyB0aGUgbGFzdCB2YWx1ZS4KCmBgYHtyfQp2YXJfNyA8LSBjb3ZfbWF0cml4WzgsIDhdCnRfNyA8LSBoeXBvdGhlc2lzX21vZGVsJGNvZWZmaWNpZW50c1s4XSAvIHNxcnQodmFyXzcpCnRfNwpgYGAKCkFuZCB0aGlzIGNvaW5jaWRlcyB3aXRoIHRoZSB2YWx1ZSBnaXZlbiBpbiB0aGUgYHN1bW1hcnlgLgoKRm9yIHRoZSBzZWNvbmQgdGVzdCwgd2UgY29tcHV0ZSB0aGUgJEYkIHZhbHVlIGFzOgoKJCQKRiA9IFxmcmFjezF9e3J9IHtcaGF0e1xiZXRhfX1ee1R9IFx3aWRlaGF0e1x0ZXh0e0Nvdn0gXGxlZnQgKCBcaGF0e1xiZXRhfSBccmlnaHQgKX1eey0xfSBcaGF0e1xiZXRhfSBcc2ltIEZfezEsIG4gLSBwfQokJAoKV2l0aCB0aGUgJFxoYXR7XGJldGF9JCBkZWZpbmUgaW4gdGhlIHRlc3QgJFxsZWZ0ICggXGJlZ2lue21hdHJpeH0KXGhhdHtcYmV0YV97NX19IFxcClxoYXR7XGJldGFfezZ9fQpcZW5ke21hdHJpeH0gXHJpZ2h0ICkkLCB0aGVuICRyPTIkIGFuZCAkbiAtIHAgPSBuIC0gOCQ6CgpgYGB7cn0KZGYgPC0gbnJvdyhtdW5pY2hfcmVudF9pbmRleF8wMSkgLSA4CnIgPC0gMgpiZXRhX2hhdCA8LSBhcy5tYXRyaXgoaHlwb3RoZXNpc19tb2RlbCRjb2VmZmljaWVudHNbNjo3XSkKZl81NiA8LSAoMS8yKSAqIHQoYmV0YV9oYXQpICUqJSBzb2x2ZShjb3ZfbWF0cml4WzY6NywgNjo3XSkgJSolIGJldGFfaGF0CmZfNTYKYGBgCgpXZSBnZXQgZXhwZWN0ZWQgJEYkOgoKYGBge3J9CmZfY3JpdCA8LSBxZihwID0gMC45NSwgciwgZGYpCmZfY3JpdApgYGAKCkFuZCB3ZSByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcy4KClRoZSB0aGlyZCBhbmQgZmluYWwgdGVzdCBjb21wYXJpbmcgJFxiZXRhX3s1fSQgYW5kICRcYmV0YV97Nn0kIGJldHdlZW4gZWFjaCBvdGhlci4gV2UgbmVlZDoKCiQkClx3aWRlaGF0e1x0ZXh0e1Zhcn0oXGhhdHtcYmV0YX1fezV9IC0gXGhhdHtcYmV0YX1fezZ9KX0gPSBcd2lkZWhhdHtcdGV4dHtWYXJ9KFxoYXR7XGJldGF9X3s1fSl9ICsgXHdpZGVoYXR7XHRleHR7VmFyfShcaGF0e1xiZXRhfV97Nn0pfSAtIDIgXGNkb3QgXHdpZGVoYXR7XHRleHR7Q292fShcaGF0e1xiZXRhfV97NX0sIFxoYXR7XGJldGF9X3s2fSl9CiQkCgpVc2luZyB0aGUgJFx3aWRlaGF0e1x0ZXh0e0Nvdn0oXGhhdHtcYmV0YX0pfSQgbWF0cml4OgoKYGBge3J9CmNvdl81XzYgPC0gY292X21hdHJpeFs2LCA3XQp2YXJfNSA8LSBjb3ZfbWF0cml4WzYsIDZdCnZhcl82IDwtIGNvdl9tYXRyaXhbNywgN10KdmFyXzVfNiA8LSB2YXJfNSArIHZhcl82IC0gMiAqIGNvdl81XzYKZl81XzYgPC0gKGh5cG90aGVzaXNfbW9kZWwkY29lZmZpY2llbnRzWzZdIC0gaHlwb3RoZXNpc19tb2RlbCRjb2VmZmljaWVudHNbN10pXjIgLyB2YXJfNV82CmZfNV82CmBgYAoKQW5kIHRoaXMgJEYkIGNyaXRpY2FsLCB1c2luZyAkcj0xJDoKCmBgYHtyfQpmX2NyaXQgPC0gcWYocCA9IDAuOTUsIDEsIGRmKQpmX2NyaXQKYGBgCgpTbywgaW4gdGhpcyBjYXNlLCB3ZSBkbyBub3QgcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMuCgojIyBFeGFtcGxlIDMuMTQgTXVuaWNoIFJlbnQgSW5kZXgg4oCUIENvbmZpZGVuY2UgSW50ZXJ2YWxzCgpUaGUgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgJFxiZXRhX3s3fSQgaXMgb2J0YWluIHVzaW5nOgoKJCQKXGJldGFfezd9IFxwbSB0X3tuIC0gcH0gXGxlZnQgKDEgLSBcYWxwaGEgLyAyIFxyaWdodCApIFxjZG90IFx3aWRlaGF0e1x0ZXh0e1Zhcn0oXGhhdHtcYmV0YX1fezd9KX1eezEvMn0KJCQKCmBgYHtyfQppbnRlciA8LSBxdCgxIC0gMC4wNSAvIDIsIGRmKSAqIHNxcnQodmFyXzcpCmMoaHlwb3RoZXNpc19tb2RlbCRjb2VmZmljaWVudHNbOF0gLSBpbnRlciwgaHlwb3RoZXNpc19tb2RlbCRjb2VmZmljaWVudHNbOF0gKyBpbnRlcikKYGBgCgojIyBFeGFtcGxlIDMuMTUgTXVuaWNoIFJlbnQgSW5kZXgg4oCUIFByZWRpY3Rpb24gSW50ZXJ2YWxzCgpVc2luZyB0aGUgcHJlZGljdGlvbiBpbnRlcnZhbDoKCiQkCnt4X3swfX1ee1R9IFxjZG90IFxoYXR7XGJldGF9IFxwbSB0X3tuLXB9KDEgLSBcYWxwaGEgLyAyKSBcaGF0e1xzaWdtYX0gXGxlZnQgKCAxICsge3hfezB9fV57VH0gXGxlZnQgKFhee1R9IFggXHJpZ2h0ICleezF9IHhfezB9IFxyaWdodCApXnsxLzJ9CiQkCgpXZSBjYW4gb2J0YWluIHRoZSAkXGhhdHtcc2lnbWF9JCBkaXJlY3RseSBmcm9tIHRoZSBtb2RlbCBvciB1c2luZzoKCiQkCihuIC0gcCkgXGhhdHtcc2lnbWF9XjIgPSBcZXBzaWxvbl57VH0gXGVwc2lsb24KJCQKCmBgYHtyfQpzaWdtYV9tb2RlbCA8LSBzdW1tYXJ5KGh5cG90aGVzaXNfbW9kZWwpJHNpZ21hCnNpZ21hXzIgPC0gdChhcy5tYXRyaXgoaHlwb3RoZXNpc19tb2RlbCRyZXNpZHVhbHMpKSAlKiUgYXMubWF0cml4KGh5cG90aGVzaXNfbW9kZWwkcmVzaWR1YWxzKSAvIChucm93KG11bmljaF9yZW50X2luZGV4XzAxKSAtIDgpCnNpZ21hIDwtIHNxcnQoc2lnbWFfMikKYyhzaWdtYV9tb2RlbCwgc2lnbWEpCmBgYAoKV2UgYXJlIGFsc28gZ29pbmcgdG8gdXNlIHRoZSBkZXNpZ24gbWF0cml4OgoKYGBge3J9CmRlc2lnbl9tYXRyaXggPC0gYXMubWF0cml4KGh5cG90aGVzaXNfbW9kZWwkbW9kZWwpCmRlc2lnbl9tYXRyaXhbLCAxXSA8LSAxCmNvbG5hbWVzKGRlc2lnbl9tYXRyaXgpWzFdIDwtICIxIgpoZWFkKGRlc2lnbl9tYXRyaXgpCmBgYAoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02LCBmaWcuYWxpZ249J2NlbnRlcid9CmFyZWFfc2VxIDwtIHNlcShtaW4obXVuaWNoX3JlbnRfaW5kZXhfMDEkYXJlYSksIG1heChtdW5pY2hfcmVudF9pbmRleF8wMSRhcmVhKSwgbGVuZ3RoLm91dCA9IDEwMCkKYXJlYWludmMgPC0gKDEgLyBhcmVhX3NlcSkgLSBtZWFuKDEgLyBhcmVhX3NlcSkKCmJldGFtIDwtIGh5cG90aGVzaXNfbW9kZWwkY29lZmZpY2llbnRzCnllYXJjYyA8LSBtdW5pY2hfcmVudF9pbmRleF8wMVttdW5pY2hfcmVudF9pbmRleF8wMSR5ZWFyYyA9PSAxOTE4LCBdWzEsIF0KIyBUaHJlZSBsYXN0IHRlcm1zIHdlcmUgYWRkZWQgZm9yIGNvbXBsZXRlbmVzcwpjb25zdGFudCA8LSBiZXRhbVsxXSArIGJldGFtWzNdICogeWVhcmNjJHllYXJjbyArIGJldGFtWzRdICogeWVhcmNjJHllYXJjbzIgKyBiZXRhbVs1XSAqIHllYXJjYyR5ZWFyY28zICsgYmV0YW1bNl0gKiAwICsgYmV0YW1bN10gKiAwICsgYmV0YW1bOF0gKiAwCgpwbG90KAogIG11bmljaF9yZW50X2luZGV4XzAxJGFyZWEsCiAgbXVuaWNoX3JlbnRfaW5kZXhfMDEkcmVudHNxbV9ldXJvLAogIHhsYWIgPSAnYXJlYSBpbiBzcW0nLAogIHlsYWIgPSAnZXN0aW1hdGVkIHJlbnQgcGVyIHNxbScKKQpiZXRhXzEgPC0gaHlwb3RoZXNpc19tb2RlbCRjb2VmZmljaWVudHNbMl0KbGluZXMoYXJlYV9zZXEsIGNvbnN0YW50ICsgYmV0YV8xICogYXJlYWludmMsIGNvbCA9ICdyZWQnLCBsd2QgPSAyKQoKY29uZl9pbnRlciA8LSBxdCgxIC0gMC4wNSAvIDIsIGRmKSAqIHNxcnQoY292X21hdHJpeFsyLCAyXSkKYmV0YV9jb25mIDwtIGMoYmV0YV8xIC0gY29uZl9pbnRlciwgYmV0YV8xICsgY29uZl9pbnRlcikKbGluZXMoYXJlYV9zZXEsIGNvbnN0YW50ICsgYmV0YV9jb25mWzFdICogYXJlYWludmMsIGNvbCA9ICdkYXJrYmx1ZScsIGx3ZCA9IDIpCmxpbmVzKGFyZWFfc2VxLCBjb25zdGFudCArIGJldGFfY29uZlsyXSAqIGFyZWFpbnZjLCBjb2wgPSAnZGFya2JsdWUnLCBsd2QgPSAyKQoKeF9vIDwtIGFzLm1hdHJpeChjYmluZCgKICAxLAogIGFyZWFpbnZjLAogIHllYXJjYyR5ZWFyY28sCiAgeWVhcmNjJHllYXJjbzIsCiAgeWVhcmNjJHllYXJjbzMsCiAgMCwKICAwLAogIDAKKSkKCnByZWRfaW50ZXIgPC0gTlVMTAoKZm9yIChpIGluIHNlcV9sZW4obnJvdyh4X28pKSkKICB4X29faSA8LSBhcy5tYXRyaXgoeF9vW2ksIF0pCiAgcHJlZF9pbnRlciA8LSBjKHByZWRfaW50ZXIsIHF0KDEgLSAwLjA1IC8gMiwgZGYpICogc2lnbWEgKiBzcXJ0KAogICAgMSArICgKICAgICAgdCh4X29faSkgJSolIHNvbHZlKHQoZGVzaWduX21hdHJpeCklKiVkZXNpZ25fbWF0cml4KSAlKiUgeF9vX2kKICAgKSkKKQoKbGluZXMoYXJlYV9zZXEsIGNvbnN0YW50ICsgYmV0YV9jb25mWzFdICogYXJlYWludmMgLSBwcmVkX2ludGVyLCBjb2wgPSAnZ3JheScsIGx3ZCA9IDIpCmxpbmVzKGFyZWFfc2VxLCBjb25zdGFudCArIGJldGFfY29uZlsyXSAqIGFyZWFpbnZjICsgcHJlZF9pbnRlciwgY29sID0gJ2dyYXknLCBsd2QgPSAyKQpgYGAKClRoaXMgY2FuIGFsc28gYmUgZG9uZSB1c2luZyBgcHJlZGljdGA6CgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTYsIGZpZy5hbGlnbj0nY2VudGVyJ30KeF9vIDwtIGFzLmRhdGEuZnJhbWUoY2JpbmQoCiAgYXJlYWludmMsCiAgeWVhcmNjJHllYXJjbywKICB5ZWFyY2MkeWVhcmNvMiwKICB5ZWFyY2MkeWVhcmNvMywKICAwLAogIDAsCiAgMAopKQpjb2xuYW1lcyh4X28pIDwtIGNvbG5hbWVzKGh5cG90aGVzaXNfbW9kZWwkbW9kZWwpWzI6OF0KY29uZmlkZW5jZV9pbnRlcnZhbCA8LSBwcmVkaWN0KGh5cG90aGVzaXNfbW9kZWwsIHhfbywgaW50ZXJ2YWwgPSAiY29uZmlkZW5jZSIpCgpwbG90KAogICAgbXVuaWNoX3JlbnRfaW5kZXhfMDEkYXJlYSwKICAgIG11bmljaF9yZW50X2luZGV4XzAxJHJlbnRzcW1fZXVybywKICAgIHhsYWIgPSAnYXJlYSBpbiBzcW0nLAogICAgeWxhYiA9ICdlc3RpbWF0ZWQgcmVudCBwZXIgc3FtJwopCmxpbmVzKGFyZWFfc2VxLCBjb25maWRlbmNlX2ludGVydmFsWywgImZpdCJdLCBjb2wgPSAncmVkJywgbHdkID0gMikKbGluZXMoYXJlYV9zZXEsIGNvbmZpZGVuY2VfaW50ZXJ2YWxbLCAibHdyIl0sIGNvbCA9ICdkYXJrYmx1ZScsIGx3ZCA9IDIpCmxpbmVzKGFyZWFfc2VxLCBjb25maWRlbmNlX2ludGVydmFsWywgInVwciJdLCBjb2wgPSAnZGFya2JsdWUnLCBsd2QgPSAyKQoKcHJlZGljdGlvbl9pbnRlcnZhbCA8LSBwcmVkaWN0KGh5cG90aGVzaXNfbW9kZWwsIHhfbywgaW50ZXJ2YWwgPSAicHJlZGljdGlvbiIpCmxpbmVzKGFyZWFfc2VxLCBwcmVkaWN0aW9uX2ludGVydmFsWywgImx3ciJdLCBjb2wgPSAnZ3JheScsIGx3ZCA9IDIpCmxpbmVzKGFyZWFfc2VxLCBwcmVkaWN0aW9uX2ludGVydmFsWywgInVwciJdLCBjb2wgPSAnZ3JheScsIGx3ZCA9IDIpCmBgYAoKIyMgRXhhbXBsZSAzLjE2IFBvbHlub21pYWwgUmVncmVzc2lvbgoKU2ltdWxhdGVkIGRhdGEgZnJvbSB0aGUgcmVhbCBtb2RlbDoKCiQkCnlfe2l9ID0gLTEgKyAwLjMgeF97aX0gKyAwLjQge3hfe2l9fV57Mn0gLSAwLjgge3hfe2l9fV57M30gKyBcZXBzaWxvbl97aX0gXHRleHR7IHdpdGggfSBcZXBzaWxvbl97aX0gXHNpbSBOKDAsIHswLjA3fV57Mn0pCiQkCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEyLCBmaWcuYWxpZ249J2NlbnRlcid9CnBhcihtZnJvdyA9IGMoMywgMikpCgp4X3NlcSA8LSBzZXEoMCwgMSwgbGVuZ3RoLm91dCA9IDUwKQp0cmFpbmluZ19kYXRhIDwtIGRhdGEuZnJhbWUoCiAgeCA9IHhfc2VxLAogIHkgPSAtMSArIDAuMyAqIHhfc2VxICsgMC40ICogeF9zZXEgXiAyIC0gMC44ICogeF9zZXEgXiAzICsgcm5vcm0oNTAsIDAsIDAuMDcpCikKdmFsaWRhdGlvbl9kYXRhIDwtIGRhdGEuZnJhbWUoCiAgeCA9IHhfc2VxLAogIHkgPSAtMSArIDAuMyAqIHhfc2VxICsgMC40ICogeF9zZXEgXiAyIC0gMC44ICogeF9zZXEgXiAzICsgcm5vcm0oNTAsIDAsIDAuMDcpCikKCnBsb3RfdHJhaW5pbmdfZGF0YSA8LSBmdW5jdGlvbih0aXRsZSkgewogIHBsb3QoCiAgICB0cmFpbmluZ19kYXRhLAogICAgbWFpbiA9IHRpdGxlLAogICAgeGxhYiA9ICd4JywKICAgIHlsYWIgPSAneScKICApCn0KCnBsb3RfdHJhaW5pbmdfZGF0YSgndHJhaW5pbmdfZGF0YScpCnBsb3QoCiAgdmFsaWRhdGlvbl9kYXRhLAogIG1haW4gPSAndmFsaWRhdGlvbl9kYXRhJywKICB4bGFiID0gJ3gnLAogIHlsYWIgPSAneScKKQoKYWRqdXN0X3BvbHlub21pYWwgPC0gZnVuY3Rpb24obCkgewogIG1vZGVsIDwtICJ5IH4geCIKICBpZiAobCAhPSAxKSB7CiAgICBmb3IgKGkgaW4gMjpsKXsKICAgICAgbW9kZWwgPC0gcGFzdGUwKG1vZGVsLCAiICsgSSh4XiIsIGksICIpIikKICAgIH0KICB9CiAgbG0obW9kZWwsIGRhdGEgPSB0cmFpbmluZ19kYXRhKQp9Cgpwb2x5bm9taWFsX21vZGVscyA8LSBsYXBwbHkoMTo5LCBhZGp1c3RfcG9seW5vbWlhbCkKcGxvdF90cmFpbmluZ19kYXRhKCdyZWdyZXNzaW9uIGxpbmUnKQpsaW5lcyh4X3NlcSwgcHJlZGljdChwb2x5bm9taWFsX21vZGVsc1sxXVtbMV1dKSwgY29sID0gJ3JlZCcsIGx3ZCA9IDIpCgpwbG90X3RyYWluaW5nX2RhdGEoJ3BvbHlub21pYWwgcmVncmVzc2lvbiB3aXRoIGw9MicpCmxpbmVzKHhfc2VxLCBwcmVkaWN0KHBvbHlub21pYWxfbW9kZWxzWzJdW1sxXV0pLCBjb2wgPSAncmVkJywgbHdkID0gMikKCnBsb3RfdHJhaW5pbmdfZGF0YSgncG9seW5vbWlhbCByZWdyZXNzaW9uIHdpdGggbD01JykKbGluZXMoeF9zZXEsIHByZWRpY3QocG9seW5vbWlhbF9tb2RlbHNbNV1bWzFdXSksIGNvbCA9ICdyZWQnLCBsd2QgPSAyKQoKbWVhbl9zcXVhcmVfZXJyb3IgPC0gZnVuY3Rpb24obW9kZWwsIGRhdGEpewogICgxIC8gNTApICogc3VtKChkYXRhJHkgLSBwcmVkaWN0KG1vZGVsLCBuZXdfZGF0YSA9IGRhdGEpKSBeIDIpCn0KCm1zZV90cmFpbmluZ19kYXRhIDwtIHVubGlzdChsYXBwbHkocG9seW5vbWlhbF9tb2RlbHMsIG1lYW5fc3F1YXJlX2Vycm9yLCBkYXRhID0gdHJhaW5pbmdfZGF0YSkpCm1zZV92YWxpZGF0aW9uX2RhdGEgPC0gdW5saXN0KGxhcHBseShwb2x5bm9taWFsX21vZGVscywgbWVhbl9zcXVhcmVfZXJyb3IsIGRhdGEgPSB2YWxpZGF0aW9uX2RhdGEpKQoKcGxvdCgKICBzZXEoMSwgOSwgMSksCiAgbXNlX3RyYWluaW5nX2RhdGEsCiAgeWxpbSA9IGMobWluKG1zZV90cmFpbmluZ19kYXRhLCBtc2VfdmFsaWRhdGlvbl9kYXRhKSwgbWF4KG1zZV90cmFpbmluZ19kYXRhLCBtc2VfdmFsaWRhdGlvbl9kYXRhKSksCiAgbWFpbiA9ICdNU0UgZm9yIHRyYWluaW5nIGFuZCB2YWxpZGF0aW9uIGRhdGEnLAogIHhsYWIgPSAnZGVncmVlIG9mIHBvbHlub21pYWwnLAogIHlsYWIgPSAnTVNFJywKICB0eXBlID0gJ2wnLAogIGNvbCA9ICdkYXJrYmx1ZScsCiAgbHdkID0gMgopCmxpbmVzKHNlcSgxLCA5LCAxKSwgbXNlX3ZhbGlkYXRpb25fZGF0YSwgY29sID0gJ3JlZCcsIGx3ZCA9IDIpCmBgYAoKIyMgRXhhbXBsZSAzLjE3IENvcnJlbGF0ZWQgQ292YXJpYXRlcwoKU2ltdWxhdGUgdGhlIGRhdGEgYXMgZm9sbG93aW5nOgoKJCQKeF97MX0gXHNpbSBVKDAsIDEpIFx3ZWRnZSB4X3szfSBcc2ltIFUoMCwgMSkKJCQKCiQkCnhfezJ9ID0geF97MX0gKyB1IFx0ZXh0eyB3aXRoIH0gdSBcc2ltIFUoMCwgMSkKJCQKCiQkCnkgXHNpbSBOKC0xICsgMC4zIHhfezF9ICsgMC4yIHhfezN9LCB7MC4yfV57Mn0pCiQkCgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OCwgZmlnLmFsaWduPSdjZW50ZXInfQpuIDwtIDE1MAoKeDEgPC0gcnVuaWYobiwgMCwgMSkKeDMgPC0gcnVuaWYobiwgMCwgMSkKdSA8LSBydW5pZihuLCAwLCAxKQoKeDIgPC0geDEgKyB1Cgp5IDwtIHJub3JtKG4sIG1lYW4gPSAtMSArIDAuMyAqIHgxICsgMC4yICogeDMsIDAuMikKCmNvcnJfY292X2RhdGEgPC0gZGF0YS5mcmFtZSgKICB5ID0geSwKICB4MSA9IHgxLAogIHgyID0geDIsCiAgeDMgPSB4MwopCnBhaXJzKGNvcnJfY292X2RhdGEpCmBgYAoKQWRqdXN0IHRoZSBtb2RlbCB3aXRoICR4X3sxfSQsICR4X3syfSQgYW5kICR4X3szfSQ6CgpgYGB7cn0Kd3JvbmdfbW9kZWwgPC0gbG0oeSB+IHgxICsgeDIgKyB4MywgZGF0YSA9IGNvcnJfY292X2RhdGEpCnN1bW1hcnkod3JvbmdfbW9kZWwpCmBgYAoKQWRqdXN0IHRoZSBtb2RlbCB3aXRoICR4X3sxfSQgYW5kICR4X3szfSQ6CgpgYGB7cn0KY29ycmVjdF9tb2RlbCA8LSBsbSh5IH4geDEgKyB4MywgZGF0YSA9IGNvcnJfY292X2RhdGEpCnN1bW1hcnkoY29ycmVjdF9tb2RlbCkKYGBgCgojIyBFeGFtcGxlIDMuMTggUG9seW5vbWlhbCBSZWdyZXNzaW9uIOKAlCBNb2RlbCBDaG9pY2Ugd2l0aCBBSUMKCldlIGNhbGN1bGF0ZSBBSUMgdXNpbmc6CgokJApBSUMgPSBuIFxjZG90IFxsb2coXGhhdHtcc2lnbWF9XnsyfSkgKyAgMiBcbGVmdCAoIFxsZWZ0IHwgTSBccmlnaHQgfCArIDEgXHJpZ2h0ICkKJCQKCmBgYHtyLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD00LCBmaWcuYWxpZ249J2NlbnRlcid9CmFpYyA8LSBmdW5jdGlvbihtb2RlbCl7CiAgbSA8LSBuY29sKG1vZGVsJG1vZGVsKSAtIDEKICBuIDwtIG5yb3cobW9kZWwkbW9kZWwpCiAgbiAqIGxvZyhzdW1tYXJ5KG1vZGVsKSRzaWdtYV4yKSArIDIgKiAobSArIDEpCn0KCmFpY192YWx1ZXMgPC0gdW5saXN0KGxhcHBseShwb2x5bm9taWFsX21vZGVscywgYWljKSkKcGxvdCgKICBhaWNfdmFsdWVzLAogIG1haW4gPSAnQUlDIGFzIGEgZnVuY3Rpb24gb2YgZGVncmVlIG9mIHBvbHlub21pYWxzJywKICB4bGFiID0gJ2RlZ3JlZXMgb2YgcG9seW5vbWlhbCcsCiAgeWxhYiA9ICdhaWMnLAogIHR5cGUgPSAnbCcsCiAgY29sID0gJ2RhcmtibHVlJywKICBsd2QgPSAyCikKYGBgCgpBcyBhbHdheXMsIHRoaXMgY2FuIGFsc28gYmUgZG9uZSB1c2luZyBSOgoKYGBge3IsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTQsIGZpZy5hbGlnbj0nY2VudGVyJ30KYWljIDwtIGZ1bmN0aW9uKG1vZGVsKXsKICBBSUMobW9kZWwpCn0KCmFpY192YWx1ZXMgPC0gdW5saXN0KGxhcHBseShwb2x5bm9taWFsX21vZGVscywgYWljKSkKcGxvdCgKICAgICAgICBhaWNfdmFsdWVzLAogICAgICAgIG1haW4gPSAnQUlDIGFzIGEgZnVuY3Rpb24gb2YgZGVncmVlIG9mIHBvbHlub21pYWxzJywKICAgICAgICB4bGFiID0gJ2RlZ3JlZXMgb2YgcG9seW5vbWlhbCcsCiAgICAgICAgeWxhYiA9ICdhaWMnLAogICAgICAgIHR5cGUgPSAnbCcsCiAgICAgICAgY29sID0gJ2RhcmtibHVlJywKICAgICAgICBsd2QgPSAyCikKYGBgCgojIyBFeGFtcGxlIDMuMTkgUHJpY2VzIG9mIFVzZWQgQ2FycyDigJQgTW9kZWwgQ2hvaWNlCgpBcyBmb3IgYWxsIGRhdGFzZXQsIHdlIGltcG9ydCB0aGUgXyJwcmljZXMgb2YgdXNlZCBjYXJzIl8gZGF0YXNldCBmcm9tIHRoZSBbb2ZmaWNpYWwgcGFnZSBvZiB0aGUgZGF0YXNldF0oaHR0cHM6Ly93d3cudW5pLWdvZXR0aW5nZW4uZGUvZW4vNTUxNjI1Lmh0bWwpOgoKYGBge3J9CmNhcnNfdXJsIDwtICJodHRwczovL3d3dy51bmktZ29ldHRpbmdlbi5kZS9kZS9kb2N1bWVudC9kb3dubG9hZC8wNjJjYWRmZGExYzRjMjk1Zjk0NjBiNDljN2Y1Nzk5ZS5yYXcvZ29sZmZ1bGwucmF3IgoKdXNlZF9jYXJzIDwtIHJlYWQudGFibGUoCiAgdXJsKGNhcnNfdXJsKSwKICBoZWFkZXIgPSAxLAogIGNvbENsYXNzZXMgPSBjKAogICAgIm51bWVyaWMiLCAiaW50ZWdlciIsICJudW1lcmljIiwKICAgICJpbnRlZ2VyIiwgImZhY3RvciIsICJmYWN0b3IiLAogICAgIm51bWVyaWMiLCAibnVtZXJpYyIsICJudW1lcmljIiwKICAgICJudW1lcmljIiwgIm51bWVyaWMiLCAibnVtZXJpYyIKICApCikKCiMgQ29udmVydCB0byBsb2dpY2FsCnVzZWRfY2FycyRleHRyYXMxIDwtIHVzZWRfY2FycyRleHRyYXMxID09IDEKdXNlZF9jYXJzJGV4dHJhczIgPC0gdXNlZF9jYXJzJGV4dHJhczIgPT0gMQoKIyBSZWNhbGN1bGF0ZSBwb2x5bm9taWFscwpwb2x5X2tpbG9tZXRlciA8LSBwb2x5KHVzZWRfY2FycyRraWxvbWV0ZXIsIDMpCnVzZWRfY2Fyc1ssIGMoImtpbG9tZXRlcm9wMSIsICJraWxvbWV0ZXJvcDIiLCAia2lsb21ldGVyb3AzIildIDwtIHBvbHlfa2lsb21ldGVyCnBvbHlfYWdlIDwtIHBvbHkodXNlZF9jYXJzJGFnZSwgMykKdXNlZF9jYXJzWywgYygiYWdlb3AxIiwgImFnZW9wMiIsICJhZ2VvcDMiKV0gPC0gcG9seV9hZ2UKc3VtbWFyeSh1c2VkX2NhcnMpCmBgYAoKRXhwbG9yaW5nIHRoZSBkYXRhOgoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMiwgZmlnLmFsaWduPSdjZW50ZXInfQpwYXIobWZyb3cgPSBjKDMsIDIpKQoKcGxvdCgKICB1c2VkX2NhcnMkYWdlLAogIHVzZWRfY2FycyRwcmljZSwKICBtYWluID0gJ1NjYXR0ZXIgcGxvdDogc2FsZXMgcHJpY2UgdmVyc3VzIGFnZSBpbiBtb250aHMnLAogIHhsYWIgPSAnYWdlIGluIG1vbnRocycsCiAgeWxhYiA9ICdzYWxlcyBwcmluY2UgaW4gMTAwMCBFdXJvJwopCgpwbG90KAogIHVzZWRfY2FycyRraWxvbWV0ZXIsCiAgdXNlZF9jYXJzJHByaWNlLAogIG1haW4gPSAnU2NhdHRlciBwbG90OiBzYWxlcyBwcmljZSB2ZXJzdXMga2lsb21ldGVyIHJlYWRpbmcnLAogIHhsYWIgPSAna2lsb21ldGVyIHJlYWRpbmcgaW4gMTAwMCBrbScsCiAgeWxhYiA9ICdzYWxlcyBwcmluY2UgaW4gMTAwMCBFdXJvJwopCgpwbG90KAogIHVzZWRfY2FycyRUSUEsCiAgdXNlZF9jYXJzJHByaWNlLAogIG1haW4gPSAnU2NhdHRlciBwbG90OiBzYWxlcyBwcmljZSB2ZXJzdXMgbW9udGhzIHVudGlsIFRJQScsCiAgeGxhYiA9ICdtb250aHMgdW50aWwgbmV4dCBUSUEgYXBwb2ludG1lbnQnLAogIHlsYWIgPSAnc2FsZXMgcHJpbmNlIGluIDEwMDAgRXVybycKKQoKYm94cGxvdCgKICBwcmljZSB+IGV4dHJhczEsCiAgZGF0YSA9IHVzZWRfY2FycywKICBtYWluID0gJ0JveCBwbG90OiBzYWxlcyBwcmluY2Ugd2l0aCBvciB3aXRob3V0IEFCUycsCiAgeGxhYiA9ICcnLAogIHlsYWIgPSAnc2FsZXMgcHJpY2VzIGluIDEwMDAgRXVybycsCiAgbmFtZXMgPSBjKCdubyBBQlMnLCAnQUJTJykKKQoKYm94cGxvdCgKICBwcmljZSB+IGV4dHJhczIsCiAgZGF0YSA9IHVzZWRfY2FycywKICBtYWluID0gJ0JveCBwbG90OiBzYWxlcyBwcmluY2Ugd2l0aCBvciB3aXRob3V0IHN1bnJvb2YnLAogIHhsYWIgPSAnJywKICB5bGFiID0gJ3NhbGVzIHByaWNlcyBpbiAxMDAwIEV1cm8nLAogIG5hbWVzID0gYygnbm8gc3Vucm9vZicsICdzdW5yb29mJykKKQpgYGAKCldlIGV4cGxvcmUgdGhlIG1vZGVscyBzdWdnZXN0ZWQ6CgoqKiRNMSQqKgoKJCQKXGJlZ2lue21hdHJpeH0KICBcbWF0aGJie0V9KFx0ZXh0e3ByaWNlfSkgPSAmIFxiZXRhX3swfSArIFxiZXRhX3sxfSBcdGV4dHtraWxvbWV0ZXJvcDF9ICsgXGJldGFfezJ9IFx0ZXh0e2tpbG9tZXRlcm9wMn0gKyBcYmV0YV97M30gXHRleHR7a2lsb21ldGVyb3AzfSBcXAogICAmICsgXGJldGFfezR9IFx0ZXh0e2FnZW9wMX0gKyBcYmV0YV97NX0gXHRleHR7YWdlb3AyfSArIFxiZXRhX3s2fSBcdGV4dHthZ2VvcDN9ClxlbmR7bWF0cml4fQokJAoKV2l0aCAkXHRleHR7a2lsb21ldGVyb3B9aSQgdGhlICRpJCB0ZXJtIG9mIGEgcG9seW5vbWlhbCBvZiBkZWdyZWUgMyBvZiB0aGUgJFx0ZXh0e2tpbG9tZXRlcn0kIHZhcmlhYmxlLiBFcXVpdmFsZW50IHRvICRcdGV4dHthZ2VvcH1pJC4KCioqJE0yJCoqCgokJApcYmVnaW57bWF0cml4fQogIFxtYXRoYmJ7RX0oXHRleHR7cHJpY2V9KSA9ICYgXGJldGFfezB9ICsgXGJldGFfezF9IFx0ZXh0e2tpbG9tZXRlcm9wMX0gKyBcYmV0YV97Mn0gXHRleHR7a2lsb21ldGVyb3AyfSArIFxiZXRhX3szfSBcdGV4dHtraWxvbWV0ZXJvcDN9IFxcCiAgICYgKyBcYmV0YV97NH0gXHRleHR7YWdlb3AxfSArIFxiZXRhX3s1fSBcdGV4dHthZ2VvcDJ9ICsgXGJldGFfezZ9IFx0ZXh0e2FnZW9wM30gXFwKICAgJiArIFxiZXRhX3s3fSBcdGV4dHtleHRyYXMxfQpcZW5ke21hdHJpeH0KJCQKCioqJE0zJCoqCgokJApcYmVnaW57bWF0cml4fQogIFxtYXRoYmJ7RX0oXHRleHR7cHJpY2V9KSA9ICYgXGJldGFfezB9ICsgXGJldGFfezF9IFx0ZXh0e2tpbG9tZXRlcm9wMX0gKyBcYmV0YV97Mn0gXHRleHR7a2lsb21ldGVyb3AyfSArIFxiZXRhX3szfSBcdGV4dHtraWxvbWV0ZXJvcDN9IFxcCiAgICYgKyBcYmV0YV97NH0gXHRleHR7YWdlb3AxfSArIFxiZXRhX3s1fSBcdGV4dHthZ2VvcDJ9ICsgXGJldGFfezZ9IFx0ZXh0e2FnZW9wM30gXFwKICAgJiArIFxiZXRhX3s3fSBcdGV4dHtleHRyYXMyfQpcZW5ke21hdHJpeH0KJCQKCioqJE00JCoqCgokJApcYmVnaW57bWF0cml4fQogIFxtYXRoYmJ7RX0oXHRleHR7cHJpY2V9KSA9ICYgXGJldGFfezB9ICsgXGJldGFfezF9IFx0ZXh0e2tpbG9tZXRlcm9wMX0gKyBcYmV0YV97Mn0gXHRleHR7a2lsb21ldGVyb3AyfSArIFxiZXRhX3szfSBcdGV4dHtraWxvbWV0ZXJvcDN9IFxcCiAgICYgKyBcYmV0YV97NH0gXHRleHR7YWdlb3AxfSArIFxiZXRhX3s1fSBcdGV4dHthZ2VvcDJ9ICsgXGJldGFfezZ9IFx0ZXh0e2FnZW9wM30gXFwKICAgJiArIFxiZXRhX3s3fSBcdGV4dHtUSUF9ClxlbmR7bWF0cml4fQokJAoKKiokTTUkKioKCiQkClxiZWdpbnttYXRyaXh9CiAgXG1hdGhiYntFfShcdGV4dHtwcmljZX0pID0gJiBcYmV0YV97MH0gKyBcYmV0YV97MX0gXHRleHR7a2lsb21ldGVyb3AxfSArIFxiZXRhX3syfSBcdGV4dHtraWxvbWV0ZXJvcDJ9ICsgXGJldGFfezN9IFx0ZXh0e2tpbG9tZXRlcm9wM30gXFwKICAgJiArIFxiZXRhX3s0fSBcdGV4dHthZ2VvcDF9ICsgXGJldGFfezV9IFx0ZXh0e2FnZW9wMn0gKyBcYmV0YV97Nn0gXHRleHR7YWdlb3AzfSBcXAogICAmICsgXGJldGFfezd9IFx0ZXh0e2V4dHJhczF9ICsgXGJldGFfezh9IFx0ZXh0e2V4dHJhczJ9ClxlbmR7bWF0cml4fQokJAoKKiokTTYkKioKCiQkClxiZWdpbnttYXRyaXh9CiAgXG1hdGhiYntFfShcdGV4dHtwcmljZX0pID0gJiBcYmV0YV97MH0gKyBcYmV0YV97MX0gXHRleHR7a2lsb21ldGVyb3AxfSArIFxiZXRhX3syfSBcdGV4dHtraWxvbWV0ZXJvcDJ9ICsgXGJldGFfezN9IFx0ZXh0e2tpbG9tZXRlcm9wM30gXFwKICAgJiArIFxiZXRhX3s0fSBcdGV4dHthZ2VvcDF9ICsgXGJldGFfezV9IFx0ZXh0e2FnZW9wMn0gKyBcYmV0YV97Nn0gXHRleHR7YWdlb3AzfSBcXAogICAmICsgXGJldGFfezd9IFx0ZXh0e2V4dHJhczF9ICsgXGJldGFfezh9IFx0ZXh0e1RJQX0KXGVuZHttYXRyaXh9CiQkCgoqKiRNNyQqKgoKJCQKXGJlZ2lue21hdHJpeH0KICBcbWF0aGJie0V9KFx0ZXh0e3ByaWNlfSkgPSAmIFxiZXRhX3swfSArIFxiZXRhX3sxfSBcdGV4dHtraWxvbWV0ZXJvcDF9ICsgXGJldGFfezJ9IFx0ZXh0e2tpbG9tZXRlcm9wMn0gKyBcYmV0YV97M30gXHRleHR7a2lsb21ldGVyb3AzfSBcXAogICAmICsgXGJldGFfezR9IFx0ZXh0e2FnZW9wMX0gKyBcYmV0YV97NX0gXHRleHR7YWdlb3AyfSArIFxiZXRhX3s2fSBcdGV4dHthZ2VvcDN9IFxcCiAgICYgKyBcYmV0YV97N30gXHRleHR7ZXh0cmFzMn0gKyBcYmV0YV97OH0gXHRleHR7VElBfQpcZW5ke21hdHJpeH0KJCQKCioqJE04JCoqCgokJApcYmVnaW57bWF0cml4fQogIFxtYXRoYmJ7RX0oXHRleHR7cHJpY2V9KSA9ICYgXGJldGFfezB9ICsgXGJldGFfezF9IFx0ZXh0e2tpbG9tZXRlcm9wMX0gKyBcYmV0YV97Mn0gXHRleHR7a2lsb21ldGVyb3AyfSArIFxiZXRhX3szfSBcdGV4dHtraWxvbWV0ZXJvcDN9IFxcCiAgICYgKyBcYmV0YV97NH0gXHRleHR7YWdlb3AxfSArIFxiZXRhX3s1fSBcdGV4dHthZ2VvcDJ9ICsgXGJldGFfezZ9IFx0ZXh0e2FnZW9wM30gXFwKICAgJiArIFxiZXRhX3s3fSBcdGV4dHtleHRyYXMxfSArIFxiZXRhX3s4fSBcdGV4dHtleHRyYXMyfSArIFxiZXRhX3s5fSBcdGV4dHtUSUF9ClxlbmR7bWF0cml4fQokJAoKCmBgYHtyfQpiYXNlX2Nhcl9tb2RlbCA8LSAicHJpY2UgfiBraWxvbWV0ZXJvcDEgKyBraWxvbWV0ZXJvcDIgKyBraWxvbWV0ZXJvcDMgKyBhZ2VvcDEgKyBhZ2VvcDIgKyBhZ2VvcDMiCnVzZWRfY2Fyc19tb2RlbCA8LSBsaXN0KGxtKGJhc2VfY2FyX21vZGVsLCBkYXRhID0gdXNlZF9jYXJzKSkKZXh0cmFfdmFyIDwtIGMoImV4dHJhczEiLCAiZXh0cmFzMiIsICJUSUEiKQpmb3IgKGV4dHJhIGluIGV4dHJhX3Zhcil7CiAgbmV3X21vZGVsIDwtIHBhc3RlMChiYXNlX2Nhcl9tb2RlbCwgIiArICIsIGV4dHJhKQogIHVzZWRfY2Fyc19tb2RlbFtbbGVuZ3RoKHVzZWRfY2Fyc19tb2RlbCkgKyAxXV0gPC0gbG0obmV3X21vZGVsLCBkYXRhID0gdXNlZF9jYXJzKQp9CmZvciAodHVwbGUgaW4gY29tYm4oZXh0cmFfdmFyLCAyLCBzaW1wbGlmeSA9IEZBTFNFKSl7CiAgbmV3X21vZGVsIDwtIHBhc3RlMChiYXNlX2Nhcl9tb2RlbCwgIiArICIsIHR1cGxlWzFdLCAiICsgIiwgdHVwbGVbMl0pCiAgdXNlZF9jYXJzX21vZGVsW1tsZW5ndGgodXNlZF9jYXJzX21vZGVsKSArIDFdXSA8LSBsbShuZXdfbW9kZWwsIGRhdGEgPSB1c2VkX2NhcnMpCn0KYWxsX2V4dHJhcyA8LSBwYXN0ZShleHRyYV92YXIsIGNvbGxhcHNlID0gIiArICIpCnVzZWRfY2Fyc19tb2RlbFtbbGVuZ3RoKHVzZWRfY2Fyc19tb2RlbCkgKyAxXV0gPC0gbG0ocGFzdGUwKGJhc2VfY2FyX21vZGVsLCAiICsgIiwgYWxsX2V4dHJhcyksIGRhdGEgPSB1c2VkX2NhcnMpCgpjYXJzX2FpYyA8LSBkYXRhLmZyYW1lKG1vZGVsID0gc2VxKDEsIDgpLCBhaWMgPSB1bmxpc3QobGFwcGx5KHVzZWRfY2Fyc19tb2RlbCwgQUlDKSkpCmNhcnNfYWljCmBgYAoKOXRoIG1vZGVsIGFyZSBvYnRhaW5lZCB1c2luZyBfImxlYXBzIGFuZCAgYm91bmRzIl8gYWxnb3JpdGhtLCBhbHJlYWR5IGltcGxlbWVudGVkIG9uIFI6CgpgYGB7cn0KbGlicmFyeShsZWFwcykKCmxlYXBzX21vZGVsIDwtIHJlZ3N1YnNldHMocHJpY2UgfiAuIC1hZ2UgLWtpbG9tZXRlciwgZGF0YSA9IHVzZWRfY2FycywgbnZtYXggPSA0KQoKc3VtbWFyeShsZWFwc19tb2RlbCkKYGBgCgpBcyBpbiB0aGUgYm9vayB0aGUgcmVzdWx0ZWQgbW9kZWwgaXM6CgoqKiRNOSQqKgoKJCQKXG1hdGhiYntFfShcdGV4dHtwcmljZX0pID0gXGJldGFfezB9ICsgXGJldGFfezF9IFx0ZXh0e2tpbG9tZXRlcm9wMX0gKyBcYmV0YV97Mn0gXHRleHR7a2lsb21ldGVyb3AyfSArIFxiZXRhX3szfSBcdGV4dHthZ2VvcDF9ICsgXGJldGFfezR9IFx0ZXh0e2FnZW9wMn0KJCQKCldlIGFkZCBpdCB0byB0aGUgdGFibGUgYW5kIHBsb3QgdGhlIEFJQzoKCmBgYHtyLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD01LCBmaWcuYWxpZ249J2NlbnRlcid9CnNlbGVjdGVkX3ZhciA8LSBuYW1lcyh3aGljaChzdW1tYXJ5KGxlYXBzX21vZGVsKSRvdXRtYXRbNCwgXSA9PSAiKiIpKQp1c2VkX2NhcnNfbW9kZWxbW2xlbmd0aCh1c2VkX2NhcnNfbW9kZWwpICsgMSBdXSA8LSBsbSgKICBwYXN0ZTAoInByaWNlIH4gIiwgcGFzdGUoc2VsZWN0ZWRfdmFyLCBjb2xsYXBzZT0gIiArICIpKSwgZGF0YSA9IHVzZWRfY2FycwopCmNhcnNfYWljWzksIF0gPC0gYyg5LCBBSUModXNlZF9jYXJzX21vZGVsW1s5XV0pKQpjYXJzX2FpYwoKcGxvdCgKICBjYXJzX2FpYywKICBtYWluID0gJ0FJQyBmb3IgdGhlIGRpZmZlcmVudCBtb2RlbHMnLAogIHhsYWIgPSAnbW9kZWwgbnVtYmVyJywKICB5bGFiID0gJ0FJQycsCiAgeGF4dCA9ICduJwopCmF4aXMoMSwgYXQgPSAxOjkpCmBgYAoKU2hvd2luZyB0aGUgYWdlIGFuZCBraWxvbWV0ZXIgZWZmZWN0cyBhcyBpbiBFeGFtcGxlIDMuNS4KCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTQsIGZpZy5hbGlnbj0nY2VudGVyJ30KcGFyKG1mcm93ID0gYygxLCAyKSkKCmJldGFfbTkgPC0gdXNlZF9jYXJzX21vZGVsW1s5XV0kY29lZmZpY2llbnRzCgphZ2Vfc2VxIDwtIHNlcShtaW4odXNlZF9jYXJzJGFnZSksIG1heCh1c2VkX2NhcnMkYWdlKSwgbGVuZ3RoLm91dCA9IDEwMCkKa2lsb21ldGVyX3NlcSA8LSBzZXEobWluKHVzZWRfY2FycyRraWxvbWV0ZXIpLCBtYXgodXNlZF9jYXJzJGtpbG9tZXRlciksIGxlbmd0aC5vdXQgPSAxMDApCgphZ2VfZWZmZWN0IDwtIGZ1bmN0aW9uKGFnZSl7CiAgaV9wb2x5X2FnZSA8LSBwcmVkaWN0KHBvbHlfYWdlLCBhZ2UpCiAgYmV0YV9tOVs0XSAqIGlfcG9seV9hZ2VbLCAxXSArIGJldGFfbTlbNV0gKiBpX3BvbHlfYWdlWywgMl0KfQoKa2lsb21ldGVyX2VmZmVjdCA8LSBmdW5jdGlvbihraWxvbWV0ZXIpewogIGlfcG9seV9raWxsb21ldGVyIDwtIHByZWRpY3QocG9seV9raWxvbWV0ZXIsIGtpbG9tZXRlcikKICBiZXRhX205WzJdICogaV9wb2x5X2tpbGxvbWV0ZXJbLCAxXSArIGJldGFfbTlbM10gKiBpX3BvbHlfa2lsbG9tZXRlclssIDJdCn0KCnJlc2lkdWFsX2FnZSA8LSB1c2VkX2NhcnMkcHJpY2UgLSBiZXRhX205WzFdIC0ga2lsb21ldGVyX2VmZmVjdCh1c2VkX2NhcnMka2lsb21ldGVyKQpwbG90KAogIHVzZWRfY2FycyRhZ2UsCiAgcmVzaWR1YWxfYWdlLAogIG1haW4gPSAnZWZmZWN0IG9mIGFnZSBpbmNsdWRpbmcgcGFydGlhbCByZXNpZHVhbHMnLAogIHhsYWIgPSAnYWdlIGluIG1vbnRocycsCiAgeWxhYiA9ICdlZmZlY3QgLyBwYXJ0aWFsIHJlc2lkdWFscycKKQpsaW5lcyhhZ2Vfc2VxLCBhZ2VfZWZmZWN0KGFnZV9zZXEpLCBjb2wgPSAncmVkJywgbHdkID0gMikKCnJlc2lkdWFsX2tpbG9tZXRlciA8LSB1c2VkX2NhcnMkcHJpY2UgLSBiZXRhX205WzFdIC0gYWdlX2VmZmVjdCh1c2VkX2NhcnMkYWdlKQpwbG90KAogIHVzZWRfY2FycyRraWxvbWV0ZXIsCiAgcmVzaWR1YWxfYWdlLAogIG1haW4gPSAnZWZmZWN0IG9mIGtpbG9tZXRlciByZWFkaW5nIGluY2x1ZGluZyBwYXJ0aWFsIHJlc2lkdWFscycsCiAgeGxhYiA9ICdraWxvbWV0ZXIgcmVhZGluZyBpbiAxMDAwa20nLAogIHlsYWIgPSAnZWZmZWN0IC8gcGFydGlhbCByZXNpZHVhbHMnCikKbGluZXMoa2lsb21ldGVyX3NlcSwga2lsb21ldGVyX2VmZmVjdChraWxvbWV0ZXJfc2VxKSwgY29sID0gJ3JlZCcsIGx3ZCA9IDIpCmBgYAoKIyMgRXhhbXBsZSAzLjIwIFByaWNlIG9mIFVzZWQgQ2FycyDigJQgTW9kZWwgRGlhZ25vc2lzCgpUaGUgc3R1ZGVudGl6ZWQgcmVzaWR1YWxzIGFyZSBjYWxjdWxhdGVkIHdpdGggdGhlIGVxdWF0aW9uOgoKJCQKcl97aX1eeyp9ID0gXGZyYWN7XGhhdHtcZXBzaWxvbn1fe2l9fXtcaGF0e1xzaWdtYX1feyhpKX0gXHNxcnR7MSAtIGhfe2lpfX19CiQkCgpCdXQsIHlvdSBjYW4gZ2V0IHRoZW0gdXNpbmcgUjoKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9OCwgZmlnLmFsaWduPSdjZW50ZXInfQpwYXIobWZyb3cgPSBjKDIsIDIpKQoKdF9yZXNpZHVhbHMgPC0gcnN0dWRlbnQodXNlZF9jYXJzX21vZGVsW1s5XV0pCgpuIDwtIG5yb3codXNlZF9jYXJzX21vZGVsW1s5XV0kbW9kZWwpCnAgPC0gbmNvbCh1c2VkX2NhcnNfbW9kZWxbWzldXSRtb2RlbCkKdF9jcml0aWNhbCA8LSBxdCgxIC0gMC4wNSAvICgyICogbiksIG4gLSBwIC0gMSkKCnBsb3Rfc3R1ZGVudGl6ZWRfcmVzaWR1YWxzIDwtIGZ1bmN0aW9uKGFnYWluc3QsIGFnX2xhYmVsLCBzaG9ydF9sYWJlbCl7CiAgcGxvdCgKICAgIGFnYWluc3QsCiAgICB0X3Jlc2lkdWFscywKICAgIG1haW4gPSBwYXN0ZTAoJ3N0dWRlbnRpemVkIHJlc2lkdWFscyB2ZXJ1cyAnLCBzaG9ydF9sYWJlbCksCiAgICB4bGFiID0gYWdfbGFiZWwsCiAgICB5bGFiID0gJ3N0dWRlbnRpemVkIHJlc2lkdWFscycsCiAgICB5bGltID0gYyhtaW4oYygtdF9jcml0aWNhbCAtIDAuNSwgdF9yZXNpZHVhbHMpKSwgbWF4KGModF9jcml0aWNhbCArIDAuNSwgdF9yZXNpZHVhbHMpKSkKICApCiAgYWJsaW5lKGggPSAwLCBjb2wgPSAncmVkJywgbHdkID0gMikKICBhYmxpbmUoaCA9IGMoLXRfY3JpdGljYWwsIHRfY3JpdGljYWwpLCBjb2wgPSAnZ3JheScsIGx3ZCA9IDIpCn0KcGxvdF9zdHVkZW50aXplZF9yZXNpZHVhbHModXNlZF9jYXJzX21vZGVsW1s5XV0kZml0dGVkLnZhbHVlcywgJ2VzdGltYXRlZCBzYWxlcyBwcmljZScsICdlc3RpbWF0ZWQgc2FsZXMgcHJpY2UnKQpwbG90X3N0dWRlbnRpemVkX3Jlc2lkdWFscyh1c2VkX2NhcnMka2lsb21ldGVyLCAna2lsb21ldGVyIHJlYWRpbmcgaW4gMTAwMGttJywgJ2tpbG9tZXRlciByZWFkaW5nJykKcGxvdF9zdHVkZW50aXplZF9yZXNpZHVhbHModXNlZF9jYXJzJGFnZSwgJ2FnZSBpbiBtb250aHMnLCAnYWdlIGluIG1vbnRocycpCnBsb3Rfc3R1ZGVudGl6ZWRfcmVzaWR1YWxzKHVzZWRfY2FycyRUSUEsICdtb250aHMgdG8gdGhlIG5leHQgVElBIGFwcG9pbnRtZW50JywgJ21vbnRocyB0byBUSUEnKQpgYGAKCkZvciB0aGUgc3RhbmRhcmRpemVkIHJlc2lkdWFsczoKCiQkCnJfe2l9ID0gXGZyYWN7XGhhdHtcZXBzaWxvbn1fe2l9fXtcaGF0e1xzaWdtYX1cc3FydHsxIC0gaF97aWl9fX0KJCQKCkJ1dCB1c2luZyBSOgoKYGBge3IsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTUsIGZpZy5hbGlnbj0nY2VudGVyJ30Kbl9yZXNpZHVhbHMgPC0gcnN0YW5kYXJkKHVzZWRfY2Fyc19tb2RlbFtbOV1dKQoKcXFub3JtKG5fcmVzaWR1YWxzLCBjb2wgPSAnZGFya2JsdWUnLCBtYWluID0gJycsIHhsYWIgPSAncXVhbnRpbGVzIG9mIG5vcm1hbCBkaXN0cmlidXRpb24nLCB5bGFiID0gJ3F1YW50aWxlcyBvZiByZXNpZHVhbHMnKQphYmxpbmUoMCwgMSwgY29sID0gJ2JsYWNrJywgbHdkID0gMikKYGBgCgojIyBFeGFtcGxlIDMuMjEgUHJpY2VzIG9mIFVzZWQgQ2FycyDigJQgQ29sbGluZWFyaXR5CgpOb3csIHdlIHdhbnQgdGhlICRWSUYkOgoKJCQKe1ZJRn1fe2p9ID0gXGZyYWN7MX17MSAtIHtSX3tqfX1eezJ9fQokJAoKYGBge3J9CmxpYnJhcnkoY2FyKQp2aWYodXNlZF9jYXJzX21vZGVsW1s5XV0pCmBgYAoKQWxsIHZhbHVlcyAkPCAxMCQuCgojIyBFeGFtcGxlIDMuMjIgUHJpY2VzIG9mIFVzZWQgQ2Fyc+KAlE91dGxpZXJzCgpTZWUgZ3JhcGggb2YgZXhhbXBsZSAzLjIwLiBGaW5kaW5nIHRoZSBvdXRsaWVyOgoKYGBge3J9CnRfcmVzaWR1YWxzW3RfcmVzaWR1YWxzID4gdF9jcml0aWNhbF0KYGBgCgojIyBFeGFtcGxlIDMuMjMgUHJpY2VzIG9mIFVzZWQgQ2FycyDigJQgSW5mbHVlbmNlIEFuYWx5c2lzCgpHZXR0aW5nIHRoZSAkaF97aWl9JCB2YWx1ZXM6CgokJApoX3tpaX0gPSBcZnJhY3sxfXtufSArIHtcbGVmdCAoIHhfe2l9IC0gXG92ZXJsaW5le3h9IFxyaWdodCApfV57VH0gXGxlZnQgKCB7XHdpZGV0aWxkZXtYfV97an19XntUfSBcd2lkZXRpbGRle1h9X3tqfSBccmlnaHQgKV57LTF9IFxsZWZ0ICggeF97aX0gLSBcb3ZlcmxpbmV7eH0gXHJpZ2h0ICkKJCQKCmBgYHtyfQpsZXZlcmFnZSA8LSBoYXR2YWx1ZXModXNlZF9jYXJzX21vZGVsW1s5XV0pCmhlYWQobGV2ZXJhZ2UpCnJhbmdlKGxldmVyYWdlKQoKaF9jcml0aWNhbCA8LSAyICogcCAvIG4KCmJpZ19sZXZlcmFnZSA8LSBsZXZlcmFnZSA+IGhfY3JpdGljYWwKbGV2ZXJhZ2VbYmlnX2xldmVyYWdlXQpgYGAKCldlIGNhbiBwbG90IHRoZXNlIHBvaW50czoKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSdjZW50ZXInfQpwYXIobWZyb3cgPSBjKDEsIDIpKQoKcGxvdCgKICB1c2VkX2NhcnMkYWdlLAogIHVzZWRfY2FycyRwcmljZSwKICBtYWluID0gJ1NjYXR0ZXIgcGxvdDogc2FsZXMgcHJpY2UgdmVyc3VzIGFnZSBpbiBtb250aHMnLAogIHhsYWIgPSAnYWdlIGluIG1vbnRocycsCiAgeWxhYiA9ICdzYWxlcyBwcmluY2UgaW4gMTAwMCBFdXJvJwopCnBvaW50cyh1c2VkX2NhcnMkYWdlW2JpZ19sZXZlcmFnZV0sIHVzZWRfY2FycyRwcmljZVtiaWdfbGV2ZXJhZ2VdLCBjb2wgPSAncmVkJywgcGNoID0gMTYpCgpwbG90KAogIHVzZWRfY2FycyRraWxvbWV0ZXIsCiAgdXNlZF9jYXJzJHByaWNlLAogIG1haW4gPSAnU2NhdHRlciBwbG90OiBzYWxlcyBwcmljZSB2ZXJzdXMga2lsb21ldGVyIHJlYWRpbmcnLAogIHhsYWIgPSAna2lsb21ldGVyIHJlYWRpbmcgaW4gMTAwMCBrbScsCiAgeWxhYiA9ICdzYWxlcyBwcmluY2UgaW4gMTAwMCBFdXJvJwopCnBvaW50cyh1c2VkX2NhcnMka2lsb21ldGVyW2JpZ19sZXZlcmFnZV0sIHVzZWRfY2FycyRwcmljZVtiaWdfbGV2ZXJhZ2VdLCBjb2wgPSAncmVkJywgcGNoID0gMTYpCmBgYAoKQW5kIHRoZSAqKkNvb2sncyBEaXN0YW5jZSoqOgoKJCQKRF97aX0gPSBcZnJhY3t7XGxlZnQgKCBcaGF0e3l9X3soaSl9IC0gXGhhdHt5fSBccmlnaHQgKX1ee1R9IFxsZWZ0ICggXGhhdHt5fV97KGkpfSAtIFxoYXR7eX0gXHJpZ2h0ICl9e3AgXGNkb3Qge1xoYXR7XHNpZ21hfX1eezJ9fQokJAoKYGBge3J9CmNvb2sgPC0gY29va3MuZGlzdGFuY2UodXNlZF9jYXJzX21vZGVsW1s5XV0pCmhlYWQoY29vaykKcmFuZ2UoY29vaykKCmJpZ19jb29rIDwtIGNvb2sgPiAwLjUKY29va1tiaWdfY29va10KYGBgCgojIyBFeGFtcGxlIDMuMjQgUHJpY2VzIG9mIFVzZWQgQ2FycyDigJQgQWx0ZXJuYXRpdmUgTW9kZWxzCgpBZGp1c3QgbW9kZWwgOSBmb3IgYWxsIG9ic2VydmF0aW9ucywgZm9yICRcdGV4dHtraWxvbWV0ZXJ9IFxsZXEgMjMwJCBhbmQgJFx0ZXh0e2tpbG9tZXRlcn0gXGdlcSA1MCQ6CgpgYGB7ciwgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSdjZW50ZXInfQptb2RlbF8yMzAgPC0gbG0ocHJpY2UgfiBraWxvbWV0ZXJvcDEgKyBraWxvbWV0ZXJvcDIgKyBhZ2VvcDEgKyBhZ2VvcDIsIGRhdGEgPSB1c2VkX2NhcnNbdXNlZF9jYXJzJGtpbG9tZXRlciA8PSAyMzAsIF0pCm1vZGVsXzUwIDwtIGxtKHByaWNlIH4ga2lsb21ldGVyb3AxICsga2lsb21ldGVyb3AyICsgYWdlb3AxICsgYWdlb3AyLCBkYXRhID0gdXNlZF9jYXJzW3VzZWRfY2FycyRraWxvbWV0ZXIgPj0gNTAsIF0pCgpuZXdfa2lsb21ldGVyX2VmZmVjdCA8LSBmdW5jdGlvbihraWxvbWV0ZXIsIGJldGEpewogIGlfcG9seV9raWxsb21ldGVyIDwtIHByZWRpY3QocG9seV9raWxvbWV0ZXIsIGtpbG9tZXRlcikKICBiZXRhWzJdICogaV9wb2x5X2tpbGxvbWV0ZXJbLCAxXSArIGJldGFbM10gKiBpX3BvbHlfa2lsbG9tZXRlclssIDJdCn0KCnBsb3QoCiAga2lsb21ldGVyX3NlcSwKICBraWxvbWV0ZXJfZWZmZWN0KGtpbG9tZXRlcl9zZXEpLAogIG1haW4gPSAnJywKICB4bGFiID0gJ2tpbG9tZXRlciByZWFkaW5nIGluIDEwMDAga20nLAogIHlsYWIgPSAnZWZmZWN0cycsCiAgdHlwZSA9ICdsJywKICBsd2QgPSAyCikKbGluZXMoa2lsb21ldGVyX3NlcSwgbmV3X2tpbG9tZXRlcl9lZmZlY3Qoa2lsb21ldGVyX3NlcSwgbW9kZWxfMjMwJGNvZWZmaWNpZW50cyksIGNvbCA9ICdyZWQnLCBsd2QgPSAyKQpsaW5lcyhraWxvbWV0ZXJfc2VxLCBuZXdfa2lsb21ldGVyX2VmZmVjdChraWxvbWV0ZXJfc2VxLCBtb2RlbF81MCRjb2VmZmljaWVudHMpLCBjb2wgPSAnZ3JlZW4nLCBsd2QgPSAyKQoKbGVnZW5kKAogICJ0b3ByaWdodCIsCiAgbGVnZW5kID0gYygnTFMgd2l0aCBhbGwgb2JzZXJ2YXRpb24nLCAnTFMgd2l0aG91dCBraWxvbWV0ZXI+MjMwJywgJ0xTIHdpdGhvdXQga2lsb21ldGVyPDUwJyksCiAgY29sID0gYygnYmxhY2snLCAncmVkJywgJ2dyZWVuJyksCiAgbHdkID0gMgopCmBgYAo=